vue-router父子路由名称路径相同导致插入位置错误原因
export const ErrorPageRoute = {
path: '/:path(.*)*', 假设为A
name: 'ErrorPage',
component: HelloWorld,
meta: {
title: 'ErrorPage',
hideBreadcrumb: true,
},
children: [
{
path: '/:path(.*)*', 假设为B
component: HelloWorld,
name: 'ErrorPage',
meta: {
title: 'ErrorPage',
hideBreadcrumb: true,
},
},
],
};
如果父子路由路径和名称都一样,会导致父路由被删除
原因
- 路由A、B传入
addRoute
后,会调用matcher.addRoute
- 传入评分函数,给两个函数评分存在score里
- 防止重复路由会删除名为
ErrorPage
的路由 - 执行
insertMatcher
函数,首先会findInsertionIndex
查找插入路由的下标 - 通过
const insertionAncestor = getInsertionAncestor(matcher);
查找路由A的父路由 因为如下代码,路由A没有父路由,所以没有祖先,因此不会执行
if (insertionAncestor)
内把子路由插在父路由前的代码function getInsertionAncestor(matcher) { let ancestor = matcher; while ((ancestor = ancestor.parent)) { if (isMatchable(ancestor) && comparePathParserScore(matcher, ancestor) === 0) { return ancestor; } } return; }
- 下面路由A此时已经被插入进路由中,再次执行
matcher.addRoute
插入路由B - 传入评分函数评分,此时,因为AB路由
path
相同,评分也会相同 - 防止重复路由会删除名为
ErrorPage
的路由,注意,此时会导致名称相同的路由A被删除 - 执行
insertMatcher
函数,首先会findInsertionIndex
查找插入路由的下标 执行到
const insertionAncestor = getInsertionAncestor(matcher);
function getInsertionAncestor(matcher) { let ancestor = matcher; // 复制B给ancestor while ((ancestor = ancestor.parent)) { // 获取B的父路由,即A if (isMatchable(ancestor) && // 确定路由是否是可访问对象 comparePathParserScore(matcher, ancestor) === 0) { // 比较path评分,前面说过,因为path相同所以评分相同,因此比较答案是0 return ancestor; // 返回父路由A对象 } } return; }
insertionAncestor
为A,执行如下代码。(upper通过二分法评分来确定适合插入的位置,在这里为整个路由的length)const insertionAncestor = getInsertionAncestor(matcher); if (insertionAncestor) { upper = matchers.lastIndexOf(insertionAncestor, upper - 1); // 查找父路由的下标,因为查找不到所以upper是-1 if ((process.env.NODE_ENV !== 'production') && upper < 0) {...} // 生产环境不会报错,upper小于0执行 } return upper; // 注意这里,即使小于0一样会返回upper
执行
matchers.splice(index, 0, matcher);
插入路由,因为是-1所以插入位置是倒数第一个前面function insertMatcher(matcher) { const index = findInsertionIndex(matcher, matchers); matchers.splice(index, 0, matcher); // 执行此代码插入 // only add the original record to the name map if (matcher.record.name && !isAliasRecord(matcher)) // 通常isAliasRecord为false,我也不知道什么时候为true。我说的是取反之前的执行结果 matcherMap.set(matcher.record.name, matcher); // 将原始记录设置到映射表 }
从上面的执行流程可以知道,为什么名称+路径都相同时才会导致新的路由会插入到倒数第二个位置。
名称不同
父路由不会被删除
路径不同
导致评分不同,getInsertionAncestor
返回为空,无法直接插入到父路由前。会回返回upper
当做插入位置
本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。