Vue的路由在执行跳转时,根据源码可知,调用了router中定义的navigate函数
function push(to: RouteLocationRaw) {
return pushWithRedirect(to)
}
function replace(to: RouteLocationRaw) {
return push(assign(locationAsObject(to), { replace: true }))
}
function pushWithRedirect(
to: RouteLocationRaw | RouteLocation,
redirectedFrom?: RouteLocation
): Promise<NavigationFailure | void | undefined> {
// ...
return (failure ? Promise.resolve(failure) : navigate(toLocation, from))/*调用navigate*/
.catch((error: NavigationFailure | NavigationRedirectError) =>
isNavigationFailure(error)
? // navigation redirects still mark the router as ready
isNavigationFailure(error, ErrorTypes.NAVIGATION_GUARD_REDIRECT)
? error
: markAsReady(error) // also returns the error
: // reject any unknown error
triggerError(error, toLocation, from)
)
.then((failure: NavigationFailure | NavigationRedirectError | void) => {
if (failure) {
// ...
} else {
// 执行finalizeNavigation完成导航
// if we fail we don't finalize the navigation
failure = finalizeNavigation(
toLocation as RouteLocationNormalizedLoaded,
from,
true,
replace,
data
)
}
// 触发`afterEach`
triggerAfterEach(
toLocation as RouteLocationNormalizedLoaded,
from,
failure
)
return failure
})
}
function navigate(
to: RouteLocationNormalized,
from: RouteLocationNormalizedLoaded
): Promise<any> {
let guards: Lazy<any>[]
// ...
// run the queue of per route beforeRouteLeave guards
return (
// 1.调用离开组件的`beforeRouteLeave`钩子
runGuardQueue(guards)
.then(() => {
// 获取全局的的`beforeEach`钩子
// check global guards beforeEach
guards = []
for (const guard of beforeGuards.list()) {
guards.push(guardToPromiseFn(guard, to, from))
}
guards.push(canceledNavigationCheck)
// 2.调用全局的`beforeEach`钩子
return runGuardQueue(guards)
})
.then(() => {
// 获取更新的路由其对应组件的`beforeRouteUpdate`钩子
// check in components beforeRouteUpdate
guards = extractComponentsGuards(
updatingRecords,
'beforeRouteUpdate',
to,
from
)
for (const record of updatingRecords) {
record.updateGuards.forEach(guard => {
guards.push(guardToPromiseFn(guard, to, from))
})
}
guards.push(canceledNavigationCheck)
// 3.调用复用组件的`beforeRouteUpdate`钩子
// run the queue of per route beforeEnter guards
return runGuardQueue(guards)
})
.then(() => {
// 获取进入的路由自身的`beforeEnter`钩子
// check the route beforeEnter
guards = []
for (const record of enteringRecords) {
// do not trigger beforeEnter on reused views
if (record.beforeEnter) {
if (isArray(record.beforeEnter)) {
for (const beforeEnter of record.beforeEnter)
guards.push(guardToPromiseFn(beforeEnter, to, from))
} else {
guards.push(guardToPromiseFn(record.beforeEnter, to, from))
}
}
}
guards.push(canceledNavigationCheck)
// 4.调用新匹配路由的`beforeEnter`钩子
// run the queue of per route beforeEnter guards
return runGuardQueue(guards)
})
.then(() => {
// NOTE: at this point to.matched is normalized and does not contain any () => Promise<Component>
// clear existing enterCallbacks, these are added by extractComponentsGuards
to.matched.forEach(record => (record.enterCallbacks = {}))
// 获取进入的路由其对应组件的`beforeRouteEnter`钩子
// check in-component beforeRouteEnter
guards = extractComponentsGuards(
enteringRecords,
'beforeRouteEnter',
to,
from
)
guards.push(canceledNavigationCheck)
// 5.调用新组件的`beforeRouteEnter`钩子
// run the queue of per route beforeEnter guards
return runGuardQueue(guards)
})
.then(() => {
// 获取全局的的`beforeResolve`钩子
// check global guards beforeResolve
guards = []
for (const guard of beforeResolveGuards.list()) {
guards.push(guardToPromiseFn(guard, to, from))
}
guards.push(canceledNavigationCheck)
// 6.调用全局的`beforeResolve`守卫
return runGuardQueue(guards)
})
// catch any navigation canceled
.catch(err =>
isNavigationFailure(err, ErrorTypes.NAVIGATION_CANCELLED)
? err
: Promise.reject(err)
)
)
}
// 触发`afterEach`
function triggerAfterEach(
to: RouteLocationNormalizedLoaded,
from: RouteLocationNormalizedLoaded,
failure?: NavigationFailure | void
): void {
// navigation is confirmed, call afterGuards
// TODO: wrap with error handlers
afterGuards
.list()
.forEach(guard => runWithContext(() => guard(to, from, failure)))
}
由上述源码中可以看出,由Promise then的链式调用保证了路由守卫按照以下顺序执行:
- 旧的路由组件
beforeRouteLeave
- 全局配置的
beforeEach
- 复用的路由组件
beforeRouteUpdate
- 新路由的
beforeEnter
- 新路由组件的
beforeRouteEnter
- 全局配置的
beforeResolve
- navigate执行完毕后,会调用
triggerAfterEach
函数,触发afterEach
和官网文档上所写的是一样的。The Full Navigation Resolution Flow
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。