base.js
src/history/base.js
定义了History
类,VueRouter中的history,根据mode,可能是HTML5History、HashHistory或Abstract实例,其中HTML5History、HashHistory等都是继承自History类。
History
类提供了一些路由操作的基本方法:
Listen——监听callback
onReady ——监听路由是否ready,ready时,将所有cb装进readyCbs列表
onError
transitionTo ——路由的跳转,会判断跳转to的路径是否在路由表中:是,才进行组件替换,调用confirmTransition
1. constructor
构造函数
1 | constructor(router: Router, base: ?string) { |
2. transitionTo
路由跳转函数
路由的跳转,会判断跳转to的路径是否在路由表中:是,才进行组件替换,调用confirmTransition
1 | transitionTo( |
match
路由匹配函数
src/create-matcher.js
先看如何匹配路由获得路由信息:判断两步,命名路由,非命名路由,因为带有params
只能用命名路由引入,非命名路由不用判断parmas
,
1 | function match( |
_createRoute
根据上面match函数条件的不同创建不同的路由
1 | // 根据条件创建不同的路由 |
createRoute
创建路由对象
src/util/route.js
1 | export function createRoute( |
3. confirmTransition
确认过渡函数
负责控制所有的路由守卫的执行
1 | confirmTransition(route: Route, onComplete: Function, onAbort?: Function) { |
路由守卫的原理
和组件的生命周期的钩子不同,路由守卫将重点放在路由上,能够控制路由跳转,一般用在页面级别的路由跳转时控制跳转的逻辑,比如在路由守卫中检查用户是否有进入当前页面的权限,没有则跳转到授权页面,亦或是在离开页面时警告用户有未确认的信息,确认后才能跳转等等
在路由守卫中,一般会接收3个参数,to,from,next,前两个分别是跳转后和跳转前页面路由的 $route 对象,第三个参数 next 是一个函数,当执行 next 函数后会进行跳转,如果一个包含 next 参数的路由守卫里没有执行该函数,页面会无法跳转。
resolveQueue
函数 获取所有需要激活、更新、销毁的路由
1 | const { updated, deactivated, activated } = resolveQueue( |
根据跳转前和跳转后的route对象的matched数组
(当前 $route 对象以及所有父级的路由记录),返回这2个数组包含的路由记录的区别
1 | function resolveQueue( |
queue
函数 获取所有需要执行的路由守卫
数组中的守卫排列顺序是设计好的,对应vue-router官方文档中提到的路由导航解析流程
- 导航被触发
- 在失活的组件里调用离开守卫
- 调用全局的
beforeEach
守卫 - 在重用的组件里调用
beforeRouteUpdate
守卫(2.2+) - 在路由配置里调用
beforeEnter
- 解析异步路由组件
- 在被激活的组件里调用
beforeRouteEnter
- 调用全局的
beforeResolve
守卫(2.5) - 导航被确认
- 调用全局的
afterEach
钩子 - 触发DOM更新
- 用创建好的实例调用
beforeRouteEnter
守卫中传给next
的回调函数
1 | /* NavigationGuard是一个标准的路由守卫的签名,经过 queue 数组内部这些函数的转换最终会返回路由守卫组成的数组 |
分析:extractLeaveGuards
执行函数
先分析queue数组里的第一个执行函数extractLeaveGuards
,经过一层封装,最终会执行通用函数extractGuards
1 | /* |
flatMapComponents
通用函数:遍历records数组,每次执行第二个回调函数,类似于数组的map方法
在回调函数内部会执行extractGuard
函数
1 | function extractGuard( |
def为组件配置项,通过Vue核心库的函数extend将配置项转为组件构造器(虽然配置项中就能拿到对应的路由守卫,但是官方注释只有转为构造器后才能拿到一些全局混入的钩子),在生成构造器时,Vue会将配置项赋值给构造器的静态属性options,最后返回配置项中对应的路由守卫函数,即如果我们在跳转后的组件中定义了beforeRouteLeave 的话这里就会返回这个函数
最后拿到返回值guard后会经过一层处理,例如扁平化,绑定this指向;根据reverse参数决定是否要反转数组(因为matched中路由记录顺序是父=》子,而beforeRouteLeave 需要从最里层子组件触发,所以需要进行反转保证守卫触发顺序)
分析:resolveAsyncComponents
解析异步组件
异步组件:通俗来说,是指使用路由懒加载返回的路由,我们可以使用
import()
去动态加载 JS 文件,放到 vue-router 中,实现异步加载组件配置项component: ()=>import('./components/comp1)
resolveAsyncComponents
函数最终会返回一个函数,并且符合路由守卫的函数签名(可能只是为了保证返回函数的一致性,实质上在这个函数中,并不会用到 to from 这两个参数)
这个函数只是被定义了,并没有执行
首先通过flatMapComponents
遍历新增的路由记录,每次遍历都执行第二个回调函数
在回调函数里,会定义一个resolve
函数,当异步组件加载完成后,会通过 then 的形式解析 promise,最终会调用 resolve
函数并传入异步组件的配置项作为参数,resolve
函数接收到组件配置项后会像 Vue 中一样将配置项转为构造器,同时将值赋值给当前路由记录的 components 属性中(key 属性默认为 default)
另外resolveAsyncComponents
函数会通过闭包保存一个 pending 变量:代表接收的异步组件数量,在flatMapComponents
遍历的过程中,每次会将 pending 加一,而当异步组件被解析完毕后,再将 pending 减一,当 pending 为0时,代表异步组件全部解析完成,随即执行 next
方法
1 | export function resolveAsyncComponents(matched: Array<RouteRecord>): Function { |