@wy
2020-06-02T10:26:11.000000Z
字数 3056
阅读 537
源码解析
进入到 index.js 中来看在 new VueRouter 时做了哪些事。
先大致看一遍挂在类上的属性和方法,不用一上来就弄明白每个函数内部的实现,可以结合官方文档去看这些方法的用处,先有个印象。函数体代码太长,可以先折叠起来。
要掌握看代码的套路根据调用方式,从入口处着手,在脑海中运行程序,分析每进行一步运行后产生的结果,遇到函数调用或者分支条件,可以先忽略。
定义一个 VueRouter 的类,用 new 调用时, constructor 构造函数执行。在构造函数中通常会初始化一些值,留作日后访问。
class VueRouter {
// .... 其他忽略
constructor (options: RouterOptions = {}) {
this.app = null
this.apps = []
this.options = options
this.beforeHooks = [];
this.resolveHooks = []
this.afterHooks = []
this.matcher = createMatcher(options.routes || [], this)
let mode = options.mode || 'hash'
this.fallback = mode === 'history' && !supportsPushState && options.fallback !== false
if (this.fallback) {
mode = 'hash'
}
if (!inBrowser) {
mode = 'abstract'
}
this.mode = mode
switch (mode) {
case 'history':
this.history = new HTML5History(this, options.base)
break
case 'hash':
this.history = new HashHistory(this, options.base, this.fallback)
break
case 'abstract':
this.history = new AbstractHistory(this, options.base)
break
default:
if (process.env.NODE_ENV !== 'production') {
assert(false, `invalid mode: ${mode}`)
}
}
}
}
初始化时通过 options 接收参数,称之为路由配置对象,由调用者提供:
new VueRouter({ // 路由配置对象
mode: 'history',
base: '/app',
routes: [
{
path: '',
component: '',
name: '',
}
]
// ... 其他配合
})
构造函数中主要做了三件事:
在得到路由实例后,可以使用提供的全局钩子函数,例如:
let router = new VueRouter({})
router.beforeEach(() => { // 注册函数
})
router.beforeEach(() => { // 注册函数
})
router.beforeResolve(() => { // 注册函数
})
router.afterEach(() => { // 注册函数
})
如果你愿意,可以给调用同一个钩子函数注册多个函数。这些注册的函数,未来需要按照注册顺序依次执行,就需要把注册的函数存放在一个地方,那就是上面分别定义的 **.hooks 数组中。
在 index.js 中找到这三个函数,定义在类中,作为原型上的方法:
class VueRouter {
... 其余省略代码
beforeEach (fn: Function): Function {
return registerHook(this.beforeHooks, fn)
}
beforeResolve (fn: Function): Function {
return registerHook(this.resolveHooks, fn)
}
afterEach (fn: Function): Function {
return registerHook(this.afterHooks, fn)
}
}
这里三个函数做的动作是一样的,都是向数组中添加注册的函数,所以抽成了函数 registerHook 处理。
再看 registerHooks 函数实现,很简单:
function registerHook (list: Array<any>, fn: Function): Function {
list.push(fn)
return () => {
const i = list.indexOf(fn)
if (i > -1) list.splice(i, 1)
}
}
作用:接收一个数组和函数,把函数存在指定的数组中。
registerHook 执行结束后,返回一个函数。返回的函数作用是调用后,把注册的函数从数组中移除,这里很巧妙的用了闭包。
也就是说调用 beforeEach 后,返回一个函数,调用返回的函数后,可以取消注册函数。
let router = new VueRouter({})
let cancel = router.beforeEach(() => { // 注册函数
// 此钩子函数被取消,不在执行。
})
cancel();
至于多个钩子函数是怎么按照顺序执行的,后面会详细分析。
createMatcher 函数所在文件是 create-matcher.js,中,查看完整代码:;
先来看下如何使用,在 index.js 中:
class VueRouter {
constructor(options){
this.matcher = createMatcher(options.routes || [], this);
}
}
new VueRouter({
mode: 'history',
routes: [
{path: '/', component: Home},
{name: 'about', component: About}
]
})
从代码中可以看出来,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