[关闭]
@wy 2020-06-02T10:26:11.000000Z 字数 3056 阅读 537

初始化路由对象

源码解析


初始化路由对象

进入到 index.js 中来看在 new VueRouter 时做了哪些事。

先大致看一遍挂在类上的属性和方法,不用一上来就弄明白每个函数内部的实现,可以结合官方文档去看这些方法的用处,先有个印象。函数体代码太长,可以先折叠起来。

要掌握看代码的套路根据调用方式,从入口处着手,在脑海中运行程序,分析每进行一步运行后产生的结果,遇到函数调用或者分支条件,可以先忽略。

定义一个 VueRouter 的类,用 new 调用时, constructor 构造函数执行。在构造函数中通常会初始化一些值,留作日后访问。

  1. class VueRouter {
  2. // .... 其他忽略
  3. constructor (options: RouterOptions = {}) {
  4. this.app = null
  5. this.apps = []
  6. this.options = options
  7. this.beforeHooks = [];
  8. this.resolveHooks = []
  9. this.afterHooks = []
  10. this.matcher = createMatcher(options.routes || [], this)
  11. let mode = options.mode || 'hash'
  12. this.fallback = mode === 'history' && !supportsPushState && options.fallback !== false
  13. if (this.fallback) {
  14. mode = 'hash'
  15. }
  16. if (!inBrowser) {
  17. mode = 'abstract'
  18. }
  19. this.mode = mode
  20. switch (mode) {
  21. case 'history':
  22. this.history = new HTML5History(this, options.base)
  23. break
  24. case 'hash':
  25. this.history = new HashHistory(this, options.base, this.fallback)
  26. break
  27. case 'abstract':
  28. this.history = new AbstractHistory(this, options.base)
  29. break
  30. default:
  31. if (process.env.NODE_ENV !== 'production') {
  32. assert(false, `invalid mode: ${mode}`)
  33. }
  34. }
  35. }
  36. }

初始化时通过 options 接收参数,称之为路由配置对象,由调用者提供:

  1. new VueRouter({ // 路由配置对象
  2. mode: 'history',
  3. base: '/app',
  4. routes: [
  5. {
  6. path: '',
  7. component: '',
  8. name: '',
  9. }
  10. ]
  11. // ... 其他配合
  12. })

构造函数中主要做了三件事:

  1. 属性初始化数据
    a. 主要有 options 存路由配置对象;
    b. beforeHooksresolveHooksafterHooks 为数组,用来存放调用钩子函数注册的函数
  2. 调用 createMatcher 匹配器,返回两个方法 matchaddRoutes
  3. 根据传入的路由模式 mode,来决定创建哪一个模式对象。分别有 hash模式、histore 模式、abstract 模式。
    a. 说下 abstract 模式,在非浏览器环境下依然要使用路由的功能,选择这个模式。例如在 nodejs 环境、做服务端渲染时使用

收集钩子函数注册的函数

在得到路由实例后,可以使用提供的全局钩子函数,例如:

  1. let router = new VueRouter({})
  2. router.beforeEach(() => { // 注册函数
  3. })
  4. router.beforeEach(() => { // 注册函数
  5. })
  6. router.beforeResolve(() => { // 注册函数
  7. })
  8. router.afterEach(() => { // 注册函数
  9. })

如果你愿意,可以给调用同一个钩子函数注册多个函数。这些注册的函数,未来需要按照注册顺序依次执行,就需要把注册的函数存放在一个地方,那就是上面分别定义的 **.hooks 数组中。

index.js 中找到这三个函数,定义在类中,作为原型上的方法:

  1. class VueRouter {
  2. ... 其余省略代码
  3. beforeEach (fn: Function): Function {
  4. return registerHook(this.beforeHooks, fn)
  5. }
  6. beforeResolve (fn: Function): Function {
  7. return registerHook(this.resolveHooks, fn)
  8. }
  9. afterEach (fn: Function): Function {
  10. return registerHook(this.afterHooks, fn)
  11. }
  12. }

这里三个函数做的动作是一样的,都是向数组中添加注册的函数,所以抽成了函数 registerHook 处理。

再看 registerHooks 函数实现,很简单:

  1. function registerHook (list: Array<any>, fn: Function): Function {
  2. list.push(fn)
  3. return () => {
  4. const i = list.indexOf(fn)
  5. if (i > -1) list.splice(i, 1)
  6. }
  7. }

作用:接收一个数组和函数,把函数存在指定的数组中。
registerHook 执行结束后,返回一个函数。返回的函数作用是调用后,把注册的函数从数组中移除,这里很巧妙的用了闭包。

也就是说调用 beforeEach 后,返回一个函数,调用返回的函数后,可以取消注册函数。

  1. let router = new VueRouter({})
  2. let cancel = router.beforeEach(() => { // 注册函数
  3. // 此钩子函数被取消,不在执行。
  4. })
  5. cancel();

至于多个钩子函数是怎么按照顺序执行的,后面会详细分析。

调用匹配器 createMatcher

createMatcher 函数所在文件是 create-matcher.js,中,查看完整代码:;

先来看下如何使用,在 index.js 中:

  1. class VueRouter {
  2. constructor(options){
  3. this.matcher = createMatcher(options.routes || [], this);
  4. }
  5. }
  6. new VueRouter({
  7. mode: 'history',
  8. routes: [
  9. {path: '/', component: Home},
  10. {name: 'about', component: About}
  11. ]
  12. })

从代码中可以看出来,createMatcher 接受两个参数:
- routes,路由配置对象中,路径和组件映射信息
- this,为路由对象

来到 create-matcher.js 文件中,简化后代码:

```
// ... 引入一堆所需文件

export function createMatcher(routes, router){
const { pathList, pathMap, nameMap } = createRouteMap(routes)

function addRoutes (routes) {
createRouteMap(routes, pathList, pathMap, nameMap)
}
function match(){/代码省略/}
function redirect(){/代码省略/}
function alias(){/代码省略/}
function _createRoute(){/代码省略/}
return {
match,
addRoutes
}
}
```ss

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注