一、前情回顾
在 “初始化流程” 解读的文章当中,我们分析到在createRouter
初始化路由的方法当中会调用createRouterMacher
方法创建页面路由匹配对象 matcher。
先来回顾下createRouterMatcher
这个方法主流程逻辑:
- 创建相关的路由匹配器的操作方法:addRoute, resolve, removeRoute, getRoutes, getRecordMatcher;
- 根据调用
createRouter
方法传入的参数遍历调用addRoute
初始化路由匹配器数据; - 将该些操作方法挂载到返回的对象属性当中;
// vuejs:router/packages/router/src/matcher/index.ts
export function createRouterMatcher(
routes: Readonly<RouteRecordRaw[]>,
globalOptions: PathParserOptions
): RouterMatcher {
const matchers: RouteRecordMatcher[] = []
const matcherMap = new Map<RouteRecordName, RouteRecordMatcher>()
globalOptions = mergeOptions(
{ strict: false, end: true, sensitive: false } as PathParserOptions,
globalOptions
)
function getRecordMatcher(name: RouteRecordName) {
return matcherMap.get(name)
}
function addRoute(
record: RouteRecordRaw,
parent?: RouteRecordMatcher,
originalRecord?: RouteRecordMatcher
) {
// ··· ···
}
function removeRoute(matcherRef: RouteRecordName | RouteRecordMatcher) {
// ··· ···
}
function getRoutes() {
// ··· ···
}
function resolve(location: Readonly<MatcherLocationRaw>, currentLocation: Readonly<MatcherLocation>): MatcherLocation {
// ··· ···
}
routes.forEach(route => addRoute(route))
return { addRoute, resolve, removeRoute, getRoutes, getRecordMatcher }
}
当时受到文章篇幅原因,当时仅仅是了解了创建页面路由匹配器的大致逻辑,将那部分的详细逻辑原理实现像 addRoute、removeRoute 这些操作路由配置的方法实现是放到这篇文章来讲述了,接下来就正式进入到这篇文章的主题: “对路由匹配器的实现原理详解” 当中了!
二、路由匹配器的实现
路由项的结构
在对具体的几个 api 进行源码解析之前还是先来大概的了解下两个相关的数据结构定义,会对后面的具体 api 逻辑处理有一定的帮助。
路由配置项
export interface _RouteRecordBase extends PathParserOptions {
path: string
redirect?: RouteRecordRedirectOption
alias?: string | string[]
name?: RouteRecordName
beforeEnter?:
| NavigationGuardWithThis<undefined>
| NavigationGuardWithThis<undefined>[]
meta?: RouteMeta
children?: RouteRecordRaw[]
props?: _RouteRecordProps | Record<string, _RouteRecordProps>
}
export interface RouteRecordNormalized {
path: _RouteRecordBase['path'] // 路由路径
redirect: _RouteRecordBase['redirect'] | undefined // 路由跳转重定向选项
name: _RouteRecordBase['name'] // 路由名称
components: RouteRecordMultipleViews['components'] | null | undefined // 路由视图组件
children: RouteRecordRaw[] // 子路由路径
meta: Exclude<_RouteRecordBase['meta'], void> // 路由元数据
props: Record<string, _RouteRecordProps> // 路由参数
beforeEnter: _RouteRecordBase['beforeEnter']
leaveGuards: Set<NavigationGuard> // 路由离开时的守卫订阅集合
updateGuards: Set<NavigationGuard> // 路由更新时的守卫订阅集合
enterCallbacks: Record<string, NavigationGuardNextCallback[]> // 路由进入时的回调集合
instances: Record<string, ComponentPublicInstance | undefined | null> // 路由实例对象
aliasOf: RouteRecordNormalized | undefined // 路由别名对应的路由记录
}
export type RouteRecord = RouteRecordNormalized
路由匹配项
interface PathParserParamKey {
name: string
repeatable: boolean
optional: boolean
}
export interface PathParser {
re: RegExp // 路由匹配正则
score: Array<number[]> // 记录该路由匹配器的路径排名信息,后续用来计算一个 url 地址与路由配置项的匹配度
keys: PathParserParamKey[] // 记录该路由匹配项的关键字信息
parse(path: string): PathParams | null // 将 URL 地址转换为路由对象信息方法
stringify(params: PathParams): string // 将路由对象转换为 URL 地址方法
}
export interface RouteRecordMatcher extends PathParser {
record: RouteRecord // 对应的路由配置项
parent: RouteRecordMatcher | undefined // 父路由匹配项
children: RouteRecordMatcher[] // 子路由匹配项
alias: RouteRecordMatcher[] // 路由别名
}
export interface MatcherLocation {
name: RouteRecordName | null | undefined // 路由名称
path: string // 路由路径
params: RouteParams // 路由 param 参数
meta: RouteMeta // 路由元数据
matched: RouteRecord[] // 表示与当前路由路径匹配的路由项,是一个 RouteRecord 数组
}
2.1 addRoute - 添加路由匹配项
addRoute:添加路由匹配项
- 参数:record - 需要处理的路由项,parent - 父 matcher,originalRecord - 原始 matcher
- 返回:matcher - 经过处理后的路由匹配项信息对象
// vuejs:router/packages/router/src/matcher/index.ts
function addRoute(
record: RouteRecordRaw,
parent?: RouteRecordMatcher,
originalRecord?: RouteRecordMatcher
) {
const isRootAdd = !originalRecord
const mainNormalizedRecord = normalizeRouteRecord(record) // 调用 normalizeRouteRecord 格式标准化需要添加为路由项的路由配置
mainNormalizedRecord.aliasOf = originalRecord && originalRecord.record
// 合并全局 VueRouter 配置与当前 addRoute 参数
const options: PathParserOptions = mergeOptions(globalOptions, record)
const normalizedRecords: (typeof mainNormalizedRecord)[] = [
mainNormalizedRecord,
]
// 判断当前路由配置是否含有 'alias' 字段,处理路由别名
if ('alias' in record) {
const aliases = typeof record.alias === 'string' ? [record.alias] : record.alias! // 处理路由配置项别名为数组类型
// 遍历别名列表数组
for (const alias of aliases) {
// 将每一个路由别名项经过处理组合成新的标准路由配置项对象并且合并到 normalizedRecords 标准化路由配置数组中
normalizedRecords.push(
assign({}, mainNormalizedRecord, {
components: originalRecord ? originalRecord.record.components : mainNormalizedRecord.components,
path: alias,
aliasOf: originalRecord ? originalRecord.record : mainNormalizedRecord,
}) as typeof mainNormalizedRecord
)
}
}
let matcher: RouteRecordMatcher
let originalMatcher: RouteRecordMatcher | undefined
// 遍历经过前面标准化处理后的路由配置数据进行转换路由匹配器处理
for (const normalizedRecord of normalizedRecords) {
const { path } = normalizedRecord
if (parent && path[0] !== '/') {
const parentPath = parent.record.path
const connectingSlash =
parentPath[parentPath.length - 1] === '/' ? '' : '/'
normalizedRecord.path =
parent.record.path + (path && connectingSlash + path)
}
// 创建路由匹配项
matcher = createRouteRecordMatcher(normalizedRecord, parent, options)
if (originalRecord) {
originalRecord.alias.push(matcher)
} else {
originalMatcher = originalMatcher || matcher
if (originalMatcher !== matcher) originalMatcher.alias.push(matcher)
if (isRootAdd && record.name && !isAliasRecord(matcher))
removeRoute(record.name)
}
// 如果含有子路由则递归调用 addRoute 方法进行处理
if (mainNormalizedRecord.children) {
const children = mainNormalizedRecord.children
for (let i = 0; i < children.length; i++) {
addRoute(
children[i],
matcher,
originalRecord && originalRecord.children[i]
)
}
}
originalRecord = originalRecord || matcher
// 通过 insertMatcher 方法将路由匹配器数据增加到 matchers 和 matcherMap 当中
if (
(matcher.record.components &&
Object.keys(matcher.record.components).length) ||
matcher.record.name ||
matcher.record.redirect
) {
insertMatcher(matcher)
}
}
return originalMatcher ? () => { removeRoute(originalMatcher!) } : noop
}
在addRoute
方法进行逻辑初步拆解后可以归纳成三部分的逻辑处理:
- 处理组装格式化好参数为后续转换匹配路径和添加路由项做准备;
-
- 使用
mergeOptions
方法合并 VueRouter 全局配置与addRoute
参数与创建options
对象变量; - 使用
normalizeRouteRecord
格式标准化需要添加为路由项的路由配置后封装到一个数组当中并将该数组指向为normalizedRecords
变量;
- 使用
- 判断当前要处理的路由配置项是否有
alias
别名字段进行别名的处理;
-
- 使用
in
判断需要添加的路由配置项是否有alias
别名属性,如果存在则继续进行执行处理路由别名的逻辑; - 处理该路由配置项的别名数据为数组形式,支持接下来的遍历路由别名数组操作;
- 遍历别名列表数组,将每一个路由别名项经过处理组合成新的标准路由配置项对象并且合并到 normalizedRecords 标准化路由配置数组中;
- 使用
- 遍历标准化处理路由配置数组,将每一项路由配置项转换为路由匹配器数据:
-
- 首先,生成普通路由和嵌套路由的path,然后调用
createRouteRecordMatcher
方法生成一个路由匹配项记录; - 判断如果有
children
子路由项则进行addRoute
的递归调用对子路由进行路由匹配器转换处理; - 处理好相关的路由匹配项数据后则使用
insertMatcher
方法将当前的路由匹配项数据 matcher 添加到 matchers 数组和 matcherMap 当中。
- 首先,生成普通路由和嵌套路由的path,然后调用
接下来看看createRouteRecordMatcher
这个创建路由匹配项数据的方法的具体实现:
// vuejs:router/packages/router/src/matcher/pathMatcher.ts
export function createRouteRecordMatcher(
record: Readonly<RouteRecord>,
parent: RouteRecordMatcher | undefined,
options?: PathParserOptions
): RouteRecordMatcher {
const parser = tokensToParser(tokenizePath(record.path), options)
const matcher: RouteRecordMatcher = assign(parser, {
record,
parent,
children: [],
alias: [],
})
if (parent) {
if (!matcher.record.aliasOf === !parent.record.aliasOf)
parent.children.push(matcher)
}
return matcher
}
createRouteRecordMatcher
这个方法其实主要就是调用了tokensToParser
方法获取一个经过处理的路由路由转换器数据并且将相关数据合并到这个转换器对象上创建新的matcher
路由匹配器对象;
-
tokensToParser
方法主要是对路由配置的path
路由路径通过编码、解码的方式变化到一个 token 数组、score 权重数组(该tokensToParser
方法对路由路径的匹配解析操作较为复杂,后续有机会再单独抽取进行讲解),让后续能够根据这个 token 数组来进行辨认并处理子路由、动态路由、路由参数等操作和根据这个 score 来判定该路由项的权重优先级;
- 接着判断是否有父路由对象
parent
,存在父路由的情况则进行关联操作; - 最后将这个
matcher
路由匹配器对象作为函数返回值返回。
在根据路由配置项通过createRouteRecordMatcher
转换为路由匹配项数据后会调用insertMatcher
方法将这个路由匹配项存储起来,接着我们再详细看看这个insertMatcher
添加路由匹配项数据方法的实现:
// vuejs:router/packages/router/src/matcher/index.ts
function insertMatcher(matcher: RouteRecordMatcher) {
let i = 0
// 通过 while 不断遍历和判断路由匹配项的优先级的操作来找到该路由匹配项数组插入位置的下标
while (
i < matchers.length &&
comparePathParserScore(matcher, matchers[i]) >= 0 &&
(matcher.record.path !== matchers[i].record.path ||
!isRecordChildOf(matcher, matchers[i]))
)
i++
// 通过 splice 对 matchers 数组进行路由匹配项插队保存操作
matchers.splice(i, 0, matcher)
// 通过 set 对 matcherMap Map 进行对应路由匹配项设置
if (matcher.record.name && !isAliasRecord(matcher))
matcherMap.set(matcher.record.name, matcher)
}
在insertMatcher
方法将路由匹配器保存到matchers
数组和matcherMap
map对象前会调用comparePathParserScore
方法对该需要保存的路由匹配项数据进行一个权重计算:
- 这个权重计算主要是根据前面经过
tokensToParser
处理后得出的 score 权重分数数组来判断以及路由路径长度来计算得出,这块后续有机会再一起和tokensToParser
抽取进行讲解;
在遍历排序权重后获取到数组下标后进行保存数据处理:
- 通过 splice 对 matchers 数组进行路由匹配项插队保存操作(会将权重大的插入到 matchers 数组中前面的位置);
- 通过 set 对 matcherMap Map 进行对应路由匹配项设置;
提前的理解阅读:这里为啥要区分权重呢?
通过区分权重来决定 matchers 数组中对应路由项的下标,在 comparePathParserScore 的权重排序逻辑当中我们能够知道权重越大,路由项元素在 matchers 当中位置越后,因此在 matchers.find 相关查找(例如即将要讲到的 resolve 方法)当中会更先更快遍历更少次数就能被查找到,这个也是 VueRouter 当中对性能优化的一个小体现。
至此,这个addRoute
方法实现的逻辑基本上已经解析完成。
2.2 removeRoute - 删除路由项
removeRoute:根据参数查找对应路由项并且删除该路由项
- 参数:matcherRef - 路由标识,路由项名字或是路由项对象
- 返回:void
// vuejs:router/packages/router/src/matcher/index.ts
function removeRoute(matcherRef: RouteRecordName | RouteRecordMatcher) {
if (isRouteName(matcherRef)) {
const matcher = matcherMap.get(matcherRef)
if (matcher) {
matcherMap.delete(matcherRef)
matchers.splice(matchers.indexOf(matcher), 1)
matcher.children.forEach(removeRoute)
matcher.alias.forEach(removeRoute)
}
} else {
const index = matchers.indexOf(matcherRef)
if (index > -1) {
matchers.splice(index, 1)
if (matcherRef.record.name) matcherMap.delete(matcherRef.record.name)
matcherRef.children.forEach(removeRoute)
matcherRef.alias.forEach(removeRoute)
}
}
}
能够看到其实删除路由匹配项记录的操作removeRoute
的逻辑其实并不难理解:
- 首先是根据参数的类型分别处理不同的查找对应路由匹配项;
- 找到对应的路由匹配项后调用
delete
和splice
删除 Map 和数组中的路由配置项; - 然后再递归调用
removeRoute
删除其对应的别名路由和子路由匹配项。
2.3 resolve - 获取路由的一些标准化信息
resolve:根据参数来获取对应路由的一些标准化的信息对象
- 参数:location - 要标准化处理的路由信息,currentLocation - 当前的标准化路由对象
- 返回:matcherLocation - 标准化的路由对象信息
// vuejs:router/packages/router/src/matcher/index.ts
function resolve(
location: Readonly<MatcherLocationRaw>,
currentLocation: Readonly<MatcherLocation>
): MatcherLocation {
let matcher: RouteRecordMatcher | undefined
let params: PathParams = {}
let path: MatcherLocation['path']
let name: MatcherLocation['name']
// 根据参数走不同的逻辑查找到对应的路由匹配项与处理获取相关字段信息
if ('name' in location && location.name) { // 根据路由名字查找路由匹配对象 matcher
matcher = matcherMap.get(location.name)
name = matcher.record.name
params = assign(
paramsFromLocation(
currentLocation.params,
matcher.keys.filter(k => !k.optional).map(k => k.name)
),
location.params &&
paramsFromLocation(
location.params,
matcher.keys.map(k => k.name)
)
)
path = matcher.stringify(params)
} else if ('path' in location) { // 根据路由路径查找路由匹配对象 matcher
path = location.path
matcher = matchers.find(m => m.re.test(path))
if (matcher) {
params = matcher.parse(path)!
name = matcher.record.name
}
} else { // 根据当前的路由来查找对应的路由匹配对象 matcher
matcher = currentLocation.name
? matcherMap.get(currentLocation.name)
: matchers.find(m => m.re.test(currentLocation.path))
name = matcher.record.name
params = assign({}, currentLocation.params, location.params)
path = matcher.stringify(params)
}
const matched: MatcherLocation['matched'] = []
let parentMatcher: RouteRecordMatcher | undefined = matcher
// 遍历通过不同形式获取到的路由匹配器数据构造出有父子关系的路由信息
while (parentMatcher) {
matched.unshift(parentMatcher.record) // 将当前的路由匹配项对应的路由配置项推入到 matched 数组内
parentMatcher = parentMatcher.parent // 重新赋值父路由匹配项进行下一轮循环直到找到根的匹配项
}
return {
name,
path,
params,
matched,
meta: mergeMetaFields(matched),
}
}
从上面的resolve
的源码当中能够看到经过对 if 判断逻辑进行划分类型处理后这个resolve
获取信息的实现条例逻辑也是比较清晰了。
- 会优先通过
name
或者path
两个属性对路由匹配器数据当中寻找对应的路由匹配项及相关信息;
-
- 根据 name 直接从
matcherMap
map 对象中使用get
api 通过路由名称获取对应的路由匹配项。 - 根据 path 则需要遍历
matchers
路由匹配器数组一个个通过正则test
方法对路由匹配项的reg
与需要处理的路由项的path
进行匹配查找。
- 根据 name 直接从
- 如果都没有对应属性则直接使用第二个参数传入的当前路由项(同样也是使用当前路由的
name
和path
属性来获取路由匹配器)进行处理获取相关的信息。
获取到对应的路由匹配项信息后进一步处理:
- 通过遍历路由匹配器数据构造出有父子关系的路由信息。
- 使用
mergeMetaFields
遍历合并路由配置项meta
字段内的属性并作为同名meta
属性抛出。
2.4 getRoutes 与 getRecordMatcher - 获取路由匹配器信息
getRoutes:获取所有路由匹配项数据
- 参数:void
- 返回:matchers - 所有路由匹配项数据的数组
getRecordMatcher:根据路由项名字参数查找对应的路由匹配项并且返回
- 参数:name - 路由项名字
- 返回:RouteRecordMatcher - 路由匹配项
// vuejs:router/packages/router/src/matcher/index.ts
export function createRouterMatcher(
routes: Readonly<RouteRecordRaw[]>,
globalOptions: PathParserOptions
): RouterMatcher {
const matchers: RouteRecordMatcher[] = []
const matcherMap = new Map<RouteRecordName, RouteRecordMatcher>()
globalOptions = mergeOptions(
{ strict: false, end: true, sensitive: false } as PathParserOptions,
globalOptions
)
function getRecordMatcher(name: RouteRecordName) {
return matcherMap.get(name)
}
function getRoutes() {
return matchers
}
return { getRoutes, getRecordMatcher }
}
源码的实现也是很简单的,就是在前面的已经通过addRoute
的初始化处理相关的路由匹配器信息和相关变量的基础上,getRoutes
能够直接返回完整的一个路由匹配器数组数据;而getRecordMatcher
则使用 Map 数据结构的get
方法通过传入的名字参数去搜索到对应的记录对象从而返回对应的一个路由匹配器对象数据。
三、路由匹配器的使用
3.1 内部维护路由配置信息
在 VueRouter 这个前端路由管理库当中在初始化创建全局路由对象createRouter
方法接受了相关的路由配置routes
后会在内部使用createRouterMatcher
方法对这份路由配置信息进行转换创建为对应的路由匹配器信息,在后续路由跳转对路由 url 进行解析基本都是基于这份路由匹配器数据进行;并且在初始化时候将相关路由匹配器数据的操作方法经过封装后挂载到这个全局路由对象上进行抛出外部调用。
在前面的“初始化流程”的文章当中也有提及到这块的一部分,初始化时候会通过createRouter
时候传入的routes
路由配置来生成这个内部路由匹配器相关的信息。
// vuejs:router/packages/router/src/router.ts
export function createRouter(options: RouterOptions): Router {
const matcher = createRouterMatcher(options.routes, options)
function addRoute(
parentOrRoute: RouteRecordName | RouteRecordRaw,
route?: RouteRecordRaw
) {
let parent: Parameters<(typeof matcher)['addRoute']>[1] | undefined
let record: RouteRecordRaw
if (isRouteName(parentOrRoute)) {
parent = matcher.getRecordMatcher(parentOrRoute)
record = route!
} else {
record = parentOrRoute
}
return matcher.addRoute(record, parent)
}
function removeRoute(name: RouteRecordName) {
const recordMatcher = matcher.getRecordMatcher(name)
if (recordMatcher) {
matcher.removeRoute(recordMatcher)
} else if (__DEV__) {
warn(`Cannot remove non-existent route "${String(name)}"`)
}
}
function getRoutes() {
return matcher.getRoutes().map(routeMatcher => routeMatcher.record)
}
function hasRoute(name: RouteRecordName): boolean {
return !!matcher.getRecordMatcher(name)
}
const router: Router = {
addRoute,
removeRoute,
hasRoute,
getRoutes,
// ··· ···
}
return router
}
在 VueRouter 4 中路由匹配器所维护路由配置的主要作用如下:
- 将路由路径转换为路由对象:当VueRouter接收到一个路由路径时,matcher会将其转换为一个包含路由信息的对象。这个对象包括路由路径、参数、查询字符串等信息。
- 进行路由匹配:matcher将路由对象与路由表进行匹配,找到与之匹配的路由。如果找不到匹配的路由,matcher会尝试使用“404 Not Found”路由来处理未匹配的路径。
- 处理动态路由:matcher支持动态路由,可以在运行时动态添加或删除路由。当动态路由发生变化时,matcher会更新路由表并重新进行路由匹配。
- 处理路由路径的规范化:matcher会将路由路径进行规范化,以确保路由路径的一致性。例如,将重复的斜杠去除、将路径转换为小写字母等。
3.2 导航跳转时候进行判断路由变化
extractChangingRecords
参数:to、from
返回:二维数组,该数组包含三个对象 - 需要跳转离开的路由记录 leavingRecords、更新的路由记录 updatingRecords、即将进入激活的路由记录 enteringRecords。
在 Vue Router 中,extractChangingRecords
函数通常用于在路由导航过程中确定需要执行的路由钩子函数。
例如,在路由跳转前,需要执行当前路由记录中的 beforeRouteLeave 钩子函数以及进入的路由记录中的 beforeRouteEnter 钩子函数;在路由更新时,需要执行更新的路由记录中的 beforeRouteUpdate 钩子函数。通过使用extractChangingRecords
函数,可以方便地定位到相关的路由记录进而确定需要执行的钩子函数,提高路由系统的效率和灵活性。
// vuejs:router/packages/router/src/router.ts
function extractChangingRecords(
to: RouteLocationNormalized,
from: RouteLocationNormalizedLoaded
) {
const leavingRecords: RouteRecordNormalized[] = [] // 跳转离开的路由列表
const updatingRecords: RouteRecordNormalized[] = [] // 跳转需要更新的路由列表
const enteringRecords: RouteRecordNormalized[] = [] // 跳转进入的路由列表
// 比较 当前路由 from 与 目标路由 to 的路由匹配记录数量,取较大值
const len = Math.max(from.matched.length, to.matched.length)
// 遍历前面所获取到的较大值
for (let i = 0; i < len; i++) {
const recordFrom = from.matched[i] // 从当前路由当中取出一个路由匹配记录
if (recordFrom) {
// 判断这个当前路由的路由匹配记录能否在目标路由当中找到对应记录
if (to.matched.find(record => isSameRouteRecord(record, recordFrom)))
updatingRecords.push(recordFrom) // 找到了则证明需要更新
else leavingRecords.push(recordFrom) // 没找到则证明需要离开
}
const recordTo = to.matched[i] // 从当前目标路由当中取出一个路由匹配记录
if (recordTo) {
// 判断这个目标路由的路由匹配记录能否在当前路由当中找到对应记录
if (!from.matched.find(record => isSameRouteRecord(record, recordTo))) {
enteringRecords.push(recordTo) // 若找不到则证明需要进行跳转进入
}
}
}
// 按顺序组装成一个数组进行返回
return [leavingRecords, updatingRecords, enteringRecords]
}
首先,它创建了三个空数组,分别用于存放离开的路由记录、更新的路由记录和进入的路由记录。
然后,它通过遍历遍历了当前路由 from 与 目标路由 to 的路由匹配记录两个数组的长度的较大值,比较这两个路由对象它们的路由匹配记录是否相同,以确定哪些路由记录发生了变化。
- 对于当前路由的每一个路由记录,如果在目标路由中能找到相同的记录,就将它添加到 updatingRecords 数组中,否则添加到 leavingRecords 数组中;
- 对于目标路由的每一个路由记录,如果在当前路由中找不到相同的记录,就将它添加到 enteringRecords 数组中;
最后,函数返回一个包含三个数组的元组,分别表示离开的路由记录、更新的路由记录和进入的路由记录。
章节总结
这个章节的内容相对来讲其实是相对来讲有点抽象、比较难以理解,但是也是 VueRouter 这个前端路由库能够正确、高性能衔接运行起来必不可少的一个大功能根基模块。
- 首先是路由配置的一个内部维护是基于路由匹配器实现,导航跳转都是依赖此来命中找寻对应的配置视图组件进行页面渲染;
-
- 并且在全局的 VueRouter 对象当中挂载了相关操作路由配置的方法来实现动态的对路由配置进行修改操作。
- 在导航守卫当中也是根据路由匹配器来找寻该次导航跳转所涉及的相关路由记录,后续将这些获取到的相关路由匹配记录再进行提取加工获取到对应路由的一些导航守卫钩子回调来执行触发订阅。
参考资料
相关的系列文章
- VueRouter 原理解读 - 初始化流程:juejin.cn/post/722060…
- VueRouter 原理解读 - 路由能力的原理与实现:juejin.cn/post/722118…
相关的参考资料
- Vue Router - RouterMethods:router.vuejs.org/api/interfa…