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执行)、
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异步组件。