muwoo / blogs

📚一个前端的博客。
2.32k stars 350 forks source link

vue-router 实现 -- install #23

Open muwoo opened 6 years ago

muwoo commented 6 years ago

Vue 通过 use 方法,加载VueRouter中的 install 方法。install 完成 Vue 实例对 VueRouter 的挂载过程。下面我们来分析一下具体的执行过程:

export function install (Vue) {
 // ...
  // 混入 beforeCreate 钩子
  Vue.mixin({
    beforeCreate () {
      // 在option上面存在router则代表是根组件 
      if (isDef(this.$options.router)) {
        this._routerRoot = this
        this._router = this.$options.router
        // 执行_router实例的 init 方法
        this._router.init(this)
        // 为 vue 实例定义数据劫持
        Vue.util.defineReactive(this, '_route', this._router.history.current)
      } else {
        // 非根组件则直接从父组件中获取
        this._routerRoot = (this.$parent && this.$parent._routerRoot) || this
      }
      registerInstance(this, this)
    },
    destroyed () {
      registerInstance(this)
    }
  })

  // 设置代理,当访问 this.$router 的时候,代理到 this._routerRoot._router
  Object.defineProperty(Vue.prototype, '$router', {
    get () { return this._routerRoot._router }
  })
  // 设置代理,当访问 this.$route 的时候,代理到 this._routerRoot._route
  Object.defineProperty(Vue.prototype, '$route', {
    get () { return this._routerRoot._route }
  })

  // 注册 router-view 和 router-link 组件
  Vue.component('RouterView', View)
  Vue.component('RouterLink', Link)

  // Vue钩子合并策略
  const strats = Vue.config.optionMergeStrategies
  // use the same hook merging strategy for route hooks
  strats.beforeRouteEnter = strats.beforeRouteLeave = strats.beforeRouteUpdate = strats.created
  // ...
}

在构造Vue实例的时候,我们会传入router对象:

new Vue({
  router
})

此时的router会被挂载到 Vue 的跟组件this.$options选项中。在 option 上面存在 router 则代表是根组件。如果存在this.$options,则对_routerRoot_router进行赋值操作,之后执行 _router.init()方法。

为了让 _router 的变化能及时响应页面的更新,所以又接着又调用了 Vue.util.defineReactive方法来进行getset的响应式数据定义。

然后通过 registerInstance(this, this)这个方法来实现对router-view的挂载操作:

 // 执行 vm.$options._parentVnode.data.registerRouteInstance 渲染 router-view 组件
 const registerInstance = (vm, callVal) => {
    let i = vm.$options._parentVnode
    if (isDef(i) && isDef(i = i.data) && isDef(i = i.registerRouteInstance)) {
      i(vm, callVal)
    }
  }

因为只有 router-view 组件定义了data.registerRouteInstance函数。data.registerRouteInstance 主要用来执行 render 的操作,创建 router-view 组件的 Vnode :

data.registerRouteInstance = (vm, val) => {
  // ...
  return h(component, data, children)
}

后续步骤便是为Vue全局实例注册2个属性$router$route;以及组件RouterViewRouterLink

关于Vue.config.optionMergeStrategies 参考 自定义选项合并策略。下一篇我们会接着介绍一下 VueRouter 实例化的过程 有兴趣可以移步vue-router 实现 -- new VueRouter(options)

Dream4ever commented 6 years ago

多谢分享!最后部分的下一篇文章:vue-router 实现 -- new VueRouter(options),链接给错了,给成了当前这篇文章的链接了~

byr-gdp commented 6 years ago

为了让 _router 的变化能及时响应页面的更新,所以又接着又调用了 Vue.uti.defineReactive方法来进行get和set的响应式数据定义。

Vue.uti.defineReactive 拼写错了...

muwoo commented 6 years ago

@Dream4ever @byr-gdp 谢谢,已更正

abigmiu commented 1 year ago

isDef(i) && isDef(i = i.data) && isDef(i = i.registerRouteInstance) 这一句不理解。 isDef(i = i.data) i = i.data 不是赋值操作嘛。 为啥需要将 i.data 赋值给 i。