vue-router源码随笔2

执行完Vue实例的beforeCreate钩子函数之后,接下解析<router-Link>和<router-view>这两个组件,并渲染到页面中。执行组件的render方法,

// 可以忽略,只需要知道是把to(目标组件)路径格式化,
// route 为location匹配到在Router中的路由对象,
// href为route完整的链接。这里涉及到很多比较难看的函数,要抽象出来,不要绕进去。
const { location, route, href } = router.resolve(
   this.to,
   current,
   this.append
 )

 整个render都是在围绕构造虚拟Dom的data对象。其中对slot和tag进行了处理,总的来说,可以先忽略细节,认识到作用。<router-view>是个函数组件(也就是说它只是展示,没有数据监听,

没有this,没有钩子函数,它的render函数的第二个参数为render函数执行的上下文),router-view渲染之前注册了三个钩子函数:registerRouteInstance(组件初始beforeCreate执行)、

prepatch、init,这些钩子函数会在组件patch阶段按顺序一一执行,最终渲染当前route的组件。通过上面的解析,重点在于:
const route = parent.$route    //route是怎么变化的,这个变化会导致页面重新渲染。
const matched = route.matched[depth]

 $route变化在每个History实例中的调用transitionTo引起的,

/* app 为 Vue实例*/
history.listen(route => {
      this.apps.forEach((app) => {
        app._route = route
      })
})
// 把listen赋值给History的回调函数,history具有回调函数。
listen (cb: Function) {
   this.cb = cb
}
// 这个cb在update钩子函数中执行
updateRoute (route: Route) {
    this.current = route
    this.cb && this.cb(route)
}
// _router的变化导致页面更新
Vue.util.defineReactive(this, '_route', this._router.history.current)

而updateRouter在confirmTransition()中执行,confirmTransition又是transitionTo里执行。confirmTransition是vue-router难点,它涉及到导航的守护钩子函数。

导航守护钩子函数:对照官方文档按照顺序对照源码比较好。

// deactivated为失活组件,updated需要更新的组件,activated激活的组件
const { updated, deactivated, activated } = resolveQueue(
    this.current.matched,
    route.matched
 )
const queue: Array<?NavigationGuard> = [].concat(
      // in-component leave guards
      // 执行beforeRouterLeave守卫
      extractLeaveGuards(deactivated),
      // global before hooks // 全局配置守卫
      this.router.beforeHooks,
      // in-component update hooks
      // beforeRouteUpdate守卫
      extractUpdateHooks(updated),
      // 在路由配置里调用 beforeEnter
      activated.map(m => m.beforeEnter),
      // async components
      //解析异步路由组件
      resolveAsyncComponents(activated)
    )

extractLeaveGuards(deactivated) 返回函数数组。

function bindGuard (guard: NavigationGuard, instance: ?_Vue): ?NavigationGuard {
  // instance为闭包变量,也就是说赋值的那个数组函数中,instance会一直存在
  if (instance) {
    return function boundRouteGuard () {
      return guard.apply(instance, arguments)  // 在组件实例执行钩子函数
    }
  }
}
function extractLeaveGuards (deactivated: Array<RouteRecord>): Array<?Function> {
  //bindGuard 为函数闭包
  return extractGuards(deactivated, 'beforeRouteLeave', bindGuard, true)
}

比较难看,要坚持下去,看看 extractLeaveGuards的数组是怎么形成的,extractGuards:

function extractGuard (
  def: Object | Function,
  key: string
): NavigationGuard | Array<NavigationGuard> {
  if (typeof def !== 'function') {
    // extend now so that global mixins are applied.
    def = _Vue.extend(def)  //组件继承Vue 
  }
  return def.options[key]   // 获取组件的钩子函数
}
function extractGuards (
  records: Array<RouteRecord>,
  name: string,
  bind: Function,
  reverse?: boolean
): Array<?Function> {
  // 参数是目前的router数组,和一个回调,
  const guards = flatMapComponents(records, (def, instance, match, key) => {
    const guard = extractGuard(def, name)
    if (guard) {
      return Array.isArray(guard)
        ? guard.map(guard => bind(guard, instance, match, key))
        : bind(guard, instance, match, key) // 返回执行钩子的方法。
    }
  })
  //guards 就是我们想要的函数数组,它是个一维数组。
  return flatten(reverse ? guards.reverse() : guards)
}
// flatMapComponents定义如下:
export function flatMapComponents (
  matched: Array<RouteRecord>,
  fn: Function
): Array<?Function> {
  // 对matched中每个成员的components中的每个组件用回调函数处理。
  return flatten(matched.map(m => {
    return Object.keys(m.components).map(key => fn(
      m.components[key],
      m.instances[key],
      m, key
    ))
  }))
}

 看完extractLeaveGuards,执行全局beforeHooks钩子函数,接着执行extractUpdateHooks函数,这函数和extractLeaveGuards类似。只是name为“beforeRouteUpdate”,接着是每个activated组件执行beforeEnter钩子函数,处理activated异步组件。

 

 

 

posted @ 2020-06-14 18:21  anthonyliu  阅读(197)  评论(0编辑  收藏  举报