- router-link组件 通过render函数来定义组件的输出,绕过了模版编译的过程。在render函数中定义了router-link被点击触发的事件handler,通过createElement函数渲染dom。
// 当组件的options有render时,就不进行模板编译compileToFunctions生成render渲染函数
// 直接通过你组件定义的options的render生成虚拟dom。
render (h: Function) {
// 点击触发的函数
const handler = e => {
if (guardEvent(e)) {
if (this.replace) {
router.replace(location, noop)
} else {
router.push(location, noop)
}
}
}
// 默认为a标签
if (this.tag === 'a') {
data.on = on
data.attrs = { href, 'aria-current': ariaCurrentValue }
}
// createElement函数渲染dom
return h(this.tag, data, this.$slots.default)
}
- 当router-link被点击后,会触发handler。去执行当前mode(history、hash)的history的push
push (location: RawLocation, onComplete?: Function, onAbort?: Function) {
const { current: fromRoute } = this
this.transitionTo(location, route => {
pushState(cleanPath(this.base + route.fullPath))
handleScroll(this.router, route, fromRoute, false)
onComplete && onComplete(route)
}, onAbort)
}
transitionTo (
location: RawLocation,
onComplete?: Function,
onAbort?: Function
) {
let route
// catch redirect option https://github.com/vuejs/vue-router/issues/3201
try {
route = this.router.match(location, this.current)
} catch (e) {
this.errorCbs.forEach(cb => {
cb(e)
})
// Exception should still be thrown
throw e
}
const prev = this.current
this.confirmTransition(
route,
() => {
// 设置响应式数据触发router-view视图更新
this.updateRoute(route)
onComplete && onComplete(route)
this.ensureURL()
this.router.afterHooks.forEach(hook => {
hook && hook(route, prev)
})
// fire ready cbs once
if (!this.ready) {
this.ready = true
this.readyCbs.forEach(cb => {
cb(route)
})
}
},
err => {
if (onAbort) {
onAbort(err)
}
if (err && !this.ready) {
if (!isNavigationFailure(err, NavigationFailureType.redirected) || prev !== START) {
this.ready = true
this.readyErrorCbs.forEach(cb => {
cb(err)
})
}
}
}
)
}
3.在transitionTo方法中执行this.updateRoute(route), cb函数设置vue实例上响应式_router的值,触发router-view的组件视图更新
updateRoute (route: Route) {
this.current = route
this.cb && this.cb(route)
}
// this.cb
this.apps.forEach(app => {
app._route = route
})