我曾试图相信文档网站上所写的内容。但是,它似乎并不完全正确,或者实现已更新但文档没有更新。
简述:
首先,CanDeactivate 守卫从从最深到顶部检查,CanActivate 守卫从从顶部到最深检查(它将退出 在遍历中进行虚假检查)。
其次,CanActivateChild 守卫不检查从深到上。
TL;DR
详细说明
我们应该检查源代码,看看它是如何工作的。
注意:检查的提交是:https://github.com/angular/angular/tree/edb8375a5ff15d77709ccf1759efb14091fa86a4
第 1 步 - 查看 CanActivateChild 何时被调用
source here L929.
这只是它的上级调用者runCanActivateChild 被调用的地方。
在那一行,我们可以得到一些提示,它与CanActivate 的作用相同,因为CanActivate 的高级调用者runCanActivate 是在之后调用的。
第 2 步 - 看看 runCanActivateChild 是如何工作的
L926 和 L950。
runCanActivateChild 在canActivateChecks 的迭代中被调用,与runCanActivate 的调用方式相同。这里我们知道CanActivate(我的意思是功能)和CanActivateChild共享同一个数据源——canActivateChecks。
第 3 步 - canActivateChecks 是什么以及如何处理它
那么,canActivateChecks 是什么?显然,我们可以发现它是一个CanActivate 类实例的数组。但是canActivateChecks 是如何分配的呢? Go to here L865。这是重要的部分,所以我将它们粘贴在这里。
private traverseChildRoutes(
futureNode: TreeNode<ActivatedRouteSnapshot>, currNode: TreeNode<ActivatedRouteSnapshot>|null,
contexts: ChildrenOutletContexts|null, futurePath: ActivatedRouteSnapshot[]): void {
const prevChildren = nodeChildrenAsMap(currNode);
// Process the children of the future route
futureNode.children.forEach(c => {
this.traverseRoutes(c, prevChildren[c.value.outlet], contexts, futurePath.concat([c.value]));
delete prevChildren[c.value.outlet];
});
// Process any children left from the current route (not active for the future route)
forEach(
prevChildren, (v: TreeNode<ActivatedRouteSnapshot>, k: string) =>
this.deactivateRouteAndItsChildren(v, contexts !.getContext(k)));
}
private traverseRoutes(
futureNode: TreeNode<ActivatedRouteSnapshot>, currNode: TreeNode<ActivatedRouteSnapshot>,
parentContexts: ChildrenOutletContexts|null, futurePath: ActivatedRouteSnapshot[]): void {
const future = futureNode.value;
const curr = currNode ? currNode.value : null;
const context = parentContexts ? parentContexts.getContext(futureNode.value.outlet) : null;
// reusing the node
if (curr && future._routeConfig === curr._routeConfig) {
if (this.shouldRunGuardsAndResolvers(
curr, future, future._routeConfig !.runGuardsAndResolvers)) {
this.canActivateChecks.push(new CanActivate(futurePath));
const outlet = context !.outlet !;
this.canDeactivateChecks.push(new CanDeactivate(outlet.component, curr));
} else {
// we need to set the data
future.data = curr.data;
future._resolvedData = curr._resolvedData;
}
// If we have a component, we need to go through an outlet.
if (future.component) {
this.traverseChildRoutes(
futureNode, currNode, context ? context.children : null, futurePath);
// if we have a componentless route, we recurse but keep the same outlet map.
} else {
this.traverseChildRoutes(futureNode, currNode, parentContexts, futurePath);
}
} else {
// ##### comment by e-cloud #####
if (curr) {
this.deactivateRouteAndItsChildren(currNode, context);
}
this.canActivateChecks.push(new CanActivate(futurePath));
// If we have a component, we need to go through an outlet.
if (future.component) {
this.traverseChildRoutes(futureNode, null, context ? context.children : null, futurePath);
// if we have a componentless route, we recurse but keep the same outlet map.
} else {
this.traverseChildRoutes(futureNode, null, parentContexts, futurePath);
}
}
}
有点长。但是如果你仔细阅读它,你会发现它在播放一个深度优先遍历。让我们忽略相同的路由切换。找到##### comment by e-cloud ##### 并查看主要程序。它表明它首先更新canActivateChecks,然后执行下一级遍历(整体上的预序遍历)。
您必须知道路由器将应用程序的所有路由视为 url 树。每个PreActivation 通过遍历将其future(作为树路径)分成路径段。
举一个简化的例子:
我们有未来的路线/a/b/c。
然后我们将得到 [ '/a', '/a/b', '/a/b/c' ] 为canActivateChecks
显然,canActivateChecks 代表了从future 顶部到最深处的路线
源码显示canActivateChecks是从左到右迭代的。
第 4 步 - 结论
我们可以得出结论,CanActivateChild 是从上往下运行的。
希望我解释清楚。