模拟实现vue-router

173 阅读2分钟

实现vue-router需要用到vue的基本概念

  • 插件
  • 混入
  • Vue.observable()
  • 插槽
  • render函数
  • 运行时和完整版的vue

路由模式

  • hash模式原理 把url中#后面的内容作为路径地址,我们可以直接通过location.url来切换浏览器中的地址 如果改变了#后面的内容,那么浏览器不会向服务端请求这个地址,但是他会把这个地址记录到浏览器的访问历史中
    当hash改变以后我们需要记录hash的变化并做相应的处理。当hash发生变化以后会触发hashchange事件,我们需要在 hashchange的事件中我们只需要记录当前的地址,并找根据当前的地址找到对应的组件进行渲染。
  • history模式原理 通过调用history.pushState()方法来改变地址栏,pushState方法仅仅是改变当前浏览器的地址栏,并且把的地址 记录到浏览器的访问历史,并不会真正的向服务器发送请求,通过监听popstate事件,可以监听浏览器历史的变化, 在popstate事件的处理函数中,可以记录浏览器的改变地址,要注意的是当调用pushstate或者replaceState的时候 并不会触发该事件。当点击浏览器的前进、后退按钮或者调用go和back方法的时候才会触发该事件,最后当地址改变以后要根据当前的地址找到对应的组件进行渲染。

手动实现history模式下的vue-router

实现vuerouer 插件需要定义的方法和数据有

  • options
  • routerMap
  • data
  • constructor(options)
  • install
  • init
  • initEvent
  • createRouterMap
  • initComponents
    let _Vue = null
    export default class VueRouter ({
      static install (vue){
         // 1 判断插件是否安装
         if(this.install.installed){
             return
         }
         this.install.installed = true
         // 2 把vue构造函数记录到全局变量中
         _Vue = vue
         // 3 把创建vue实例时候传入的router对象注入到vue实例上去
         _Vue.mixin({
             beforeCreate () {
                 if(this.$options.router){
                     _Vue.prototype.$router = this.$options.router
                 }
             }
         })
      }
      constructor (options) {
          this.options = options
          this.routerMap = {}
          this.data = _Vue.observable({
              current:location.pathname
          }) //把data变成响应式的
      }
      init () {
          this.careteRouterMap()
          this.initComponents(_Vue)
      }
      createRouterMap () {
        this.options.router.forEach(router=>{
            this.routerMap[router.path] = router.component
        })
      }
      //注意如果注册组件你想使用template模板
      //需要使用完整版vue vue-cli创建的vue默认不包含编译器
      //开启完成版vue 在vue.comfig.js 文件里面 配置runtimeCompiler:true
      initComponents (Vue) {
          const self = this
          Vue.component('router-link',{
              props:{
                tp:String
              }
              render (h) {
                  return h ('a',{
                      attrs:{
                          href:this.to
                      },
                      on:{
                        click:this.clickHander
                      }
                    }
                  ,[this.$solts.default])
              },
              methods:{
                  clickHander (e) {
                       history.pushState({},'',this.to)
                        this.$router.data.current = this.to
                        e.preventDefault()
                  }
              }
          })
          Vue.component('router-view',{
              render(h){
                  const component = self.routerMap[self.data.current]?self.routerMap[self.data.current]:self.routerMap['*']
                  return h(component)
              }
          })
      }
      initEvent () {
          window.addEventListener('popstate',()=>{
              this.data.current = window.loaction.pathname
          })
      }
    })