Vue Router Source Code Parsing (2) Jump of Internal Router
之前的博客我们讲了我们创建VueRouter实例并通过Vue.use应用之后,VueRouter内部如何生成一整套自己的数据结构来存储路由配置的,这次博客我们继续解读Vue Router的源码,大致内容是Vue Router的路由守卫的执行逻辑。
Route guard
This is the official address of VueRouter’s route guard: https://router.vuejs.org/zh/guide/advanced/navigation-guards.html
The order of execution of route guards during the entire route change process is as follows:
- Navigation is triggered.
- Call the’beforeRouteLeave 'guard in the inactivated component.
- Call the global beforeEach guard.
- Call the beforeRouteUpdate guard (2.2 +) in the reused component.
- Call’beforeEnter 'in the route configuration.
- Parse asynchronous routing components.
- Call’beforeRouteEnter 'in the activated component.
- Call the global beforeResolve guard (2.5 +).
- Navigation is confirmed.
- Invoke the global afterEach hook.
- Trigger DOM update.
- Call the callback function passed to next in the’beforeRouteEnter 'guard, and the created component instance will be passed in as the parameter of the callback function.
Use case
Let’s first talk about the code on which our blog is based
1 | import Vue from 'vue' |
Source code analysis
push
We start with VueRouter.prototype.push to analyze what happens after the call route switch
1 | push (location: RawLocation, onComplete?: Function, onAbort?: Function) { |
The three parameters are RawLocation, the callback function when successful and the callback function when failed. RawLocation is actually the parameter type passed by the push function when we use it formally. The specific definitions are as follows:
1 | declare type Location = { |
之前的博客我们this.history会根据mode不同实例化为不同的对象,我们以HTML5History来分析
1 | push (location: RawLocation, onComplete?: Function, onAbort?: Function) { |
The first is to get the current route: current, its type is Route
Then call the’transitionTo ‘function, pass in two parameters, the first is the location, the second is the callback function, let’s see what this’transitionTo’ does
transitionTo
Before we look at the transtitionTo function, let’s look at a few utility functions.
runQueue
The first is’runQueue ':
This function has three parameters, which are:
- queue: an array of parameters
- fn: The actual execution of the function, which also has two parameters, the first is each item in the queue, and the second is the callback function after fn is executed.
- cb: the final callback function after executing fn for each item in the queue
1 | export function runQueue (queue: Array<?NavigationGuard>, fn: Function, cb: Function) { |
extractGuards
Simply put, the role of this function is to extract the routing guards inside the component from a series of incoming RouteRecord, and then specify the context of these routing guards, that is, the this pointer through the apply function as itself. If the last parameter is true, it reverses the order of these routing guards.
The function directly exposed to the outside world is, such as extractLeaveGuards, which actually calls’extractGuards (deactivated, ‘beforeRouteLeave’, bindGuard, true) ‘, that is, from a series of deactivated components, get the’beforeRouteLeave’ function, assemble it and then reverse it.
1 | function extractGuards ( |
ConfirmTransition (Steps 1-8)
Then, ‘confirmTransition’:
This function is roughly divided into three parts:
- Determine whether to abort, if necessary, exit
Generate an array of route guards to execute - Sequentially execute an array of route guards
The first is to determine whether the incoming route and the current route are the same. If so, call abort directly and exit
1 | const current = this.current |
Then there is a piece of function logic that generates the execution order of route guards.
1 | const { updated, deactivated, activated } = resolveQueue( |
The role of the first line of’resolveQueue ‘is to compare the current route and the route to jump between the changes, its two parameters are the type of’Array < RouteRecord >’, its logic is relatively simple, is to compare the difference between the two arrays:
1 | function resolveQueue ( |
The routing guards in the queue numbers are in order:
- beforeRouteLeave function in all deactivated components and then reverse the order
- global beforeHooks function
- beforeRouteUpdate function in all updated components
- beforeEnter function in the routing configuration corresponding to all activated components
- Load all functions of asynchronous components in activated
These five steps also correspond to steps 2-6 in the execution sequence of the route guard we mentioned at the beginning
After getting the correct array of route guards execution order, it is officially executed. Here, we use the tool function: runQueue we just mentioned. The code is as follows:
1 | const iterator = (hook: NavigationGuard, next) => { |
First, the iterator function has two arguments
- Route guard to perform
- Callback function after successful execution
Then our runQueue has two nested layers:
- The first layer is first passed in the queue we generated just now, that is, the route guard function of 2-6 steps, fn is our iterator function, combined with the runQueue source code we read at the beginning, the first layer does in total The thing is to execute our route guard of 2-6 steps in turn, and each execution is completed by calling the next step in the runQueue to execute the next hook
- In the callback function after all queues are executed in the first layer, we generate a layer of runQueue function. The difference is that this time, the queue we pass in is different. This time our queue generation method is: 'const enterGuards = extractEnterGuards (activated); const queue = enterGuards.concat (this.router.resolveHooks) ', composed of two parts, the first part is the beforeRouteEnter function of all activated components, and then this.router.resolveHooks, that is, the global beforeResolve, here is the route guard order Step 7 and 8
- In the callback function of runQueue on the second layer, we can call the onComplete function of confirmTransition
Then we want to understand what this onComplete does, we need to see what function transitionTo passes when calling confirmTransition:
1 | this.confirmTransition( |
ComfirmTransition success callback (steps 9-12)
As you can see, our onComplete is actually this paragraph:
1 | this.updateRoute(route) |
- The first step is to call the updateRoute function. The inside of this function is actually the callback function passed in by history.listen function. This is more important. This callback function will modify the value of $route, and this value is responsive. It will be used in the route-view component, that is to say, changing this value will cause route-view to re-call the render function, which will start the update of the view
- Step 2 If transitionTo has an onComplete function then call
- call the afterHooks function in turn, which is actually an array of globally registered afterEach, so far, step 11 is also completed
So the last step is to re-execute beforeRouterEnter and return an instance through next. This is actually related to the callback method of beforeRouterEnter generated in the seventh step. This’extractEnterGuards’ and its other two’extractLeaveGuards’ are different from’extractUpdateHooks’. Its bind function is not simply apply, but executes the next callback function after the call is successful, as follows:
1 | function extractEnterGuards ( |
At this point, the function execution 1-12 after modifying the route has been completed, and the router-view change has been successfully triggered by the reactive method.