})
// 调整优先级
for (let i = 0, l = pathList.length; i < l; i++) {
if (pathList[i] === ‘*’) {
pathList.push(pathList.splice(i, 1)[0])
l–
i–
}
}
return {
pathList,
pathMap,
nameMap
}
}
createRouteMap
需要传入路由配置,支持传入旧路径数组和旧的 Map
这一步是为后面递归和 addRoutes
做好准备。首先用三个变量记录 path
,pathMap
,nameMap
,接着我们来看 addRouteRecord
这个核心方法。这一块代码太多了,列举几个重要的步骤
// 解析路径
const pathToRegexpOptions: PathToRegexpOptions =
route.pathToRegexpOptions || {}
// 拼接路径
const normalizedPath = normalizePath(path, parent, pathToRegexpOptions.strict)
// 记录路由信息的关键对象,后续会依此建立映射表
const record: RouteRecord = {
path: normalizedPath,
regex: compileRouteRegex(normalizedPath, pathToRegexpOptions),
// route 对应的组件
components: route.components || { default: route.component },
// 组件实例
instances: {},
name,
parent,
matchAs,
redirect: route.redirect,
beforeEnter: route.beforeEnter,
meta: route.meta || {},
props: route.props == null
? {}
: route.components
? route.props
: { default: route.props }
}
使用 recod
对象 记录路由配置有利于后续路径切换时计算出新路径,这里的 path
其实是通过传入父级 record
对象的path
和当前 path
拼接出来的 。然后 regex
使用一个库将 path
解析为正则表达式。如果 route
有子节点就递归调用 addRouteRecord
// 如果有 children 递归调用 addRouteRecord
route.children.forEach(child => {
const childMatchAs = matchAs
? cleanPath(${matchAs}/${child.path}
)
: undefined
addRouteRecord(pathList, pathMap, nameMap, child, record, childMatchAs)
})
最后映射两张表,并将 record·path
保存进 pathList
,nameMap
逻辑相似就不列举了
if (!pathMap[record.path]) {
pathList.push(record.path)
pathMap[record.path] = record
}
废了这么大劲将 pathList
和 pathMap
和 nameMap
抽出来是为啥呢? 首先 pathList
是记录路由配置所有的 path
,然后 pathMap
和 nameMap
方便我们传入 path
或者 name
快速定位到一个 record
,然后辅助后续路径切换计算路由的。
addRoutes
这是在 vue2.2.0
之后新添加的 api
,或许很多情况路由并不是写死的,需要动态添加路由。有了前面的 createRouteMap
的基础上我们只需要传入 routes
即可,他就能在原基础上修改
function addRoutes (routes) {
createRouteMap(routes, pathList, pathMap, nameMap)
}
并且看到在 createMathcer
最后返回了这个方法,所以我们就可以使用这个方法
return {
match,
addRoutes
}
match
function match (
raw: RawLocation,
currentRoute?: Route,
redirectedFrom?: Location
): Route {
…
}
接下来就是 match
方法,它接收 3 个参数,其中 raw
是 RawLocation
类型,它可以是一个 url
字符串,也可以是一个 Location
对象;currentRoute
是 Route
类型,它表示当前的路径;redirectedFrom
和重定向相关。match
方法返回的是一个路径,它的作用是根据传入的 raw
和当前的路径 currentRoute
计算出一个新的路径并返回。至于他是如何计算出这条路径的,可以详细看一下如何计算出location
的 normalizeLocation
方法和 _createRoute
方法。
小结
-
createMatcher
: 根据路由的配置描述建立映射表,包括路径、名称到路由record
的映射关系, 最重要的就是createRouteMap
这个方法,这里也是动态路由匹配和嵌套路由的原理。 -
addRoutes
: 动态添加路由配置 -
match
: 根据传入的raw
和当前的路径currentRoute
计算出一个新的路径并返回。
路由模式
vue-router
支持三种路由模式(mode):hash
、history
、abstract
,其中 abstract
是在非浏览器环境下使用的路由模式 源码地址 (https://github.com/vuejs/vue-router/blob/dev/src/index.js)。
这一部分在前面初始化 vueRouter
对象时提到过,首先拿到配置项的模式,然后根据当前传入的配置判断当前浏览器是否支持这种模式,默认 IE9
以下会降级为 hash
。然后根据不同的模式去初始化不同的 history
实例。
// 一般分两种模式 hash 和 history 路由 第三种是抽象模式不常用
let mode = options.mode || ‘hash’
// 判断当前传入的配置是否能使用 history 模式
this.fallback = mode === ‘history’ && !supportsPushState && options.fallback !== false
// 降级处理
if (this.fallback) {
mode = ‘hash’
}
if (!inBrowser) {
mode = ‘abstract’
}
this.mode = mode
// 根据模式实例化不同的 history history 对象会对路由进行管理 继承于 history class
switch (mode) {
case ‘history’:
this.history = new HTML5History(this