我试图深入理解 Angular,所以我阅读了 the docs这非常有帮助。
现在我正在研究守卫。我在文档中阅读了此声明。
The router checks the CanDeactivate and CanActivateChild guards first, from the deepest child route to the top. Then it checks the CanActivate guards from the top down to the deepest child route.
现在我很困惑,为什么angular会这样执行?
对于 CanDeactivate 和 CanActivateChild,从最深的 child 到顶部进行检查有什么好处。以及从顶部到最深的 CanActivate 子路由?
最佳答案
我曾试图相信文档站点中所写的内容。但是,它似乎并不完全正确,或者实现已更新但文档没有更新。
简而言之:
首先,CanDeactivate
守卫从最深到顶部检查,CanActivate
守卫从从顶部到最深检查(它将退出并在遍历中进行虚假检查)。
其次,CanActivateChild
守卫不会从最深到最顶部进行检查。
长话短说
详细说明
我们应该检查源代码以了解它是如何工作的。
Note: the commit checked is: https://github.com/angular/angular/tree/edb8375a5ff15d77709ccf1759efb14091fa86a4
第 1 步 - 查看 CanActivateChild
何时被调用
这只是它的上级调用者 runCanActivateChild
被调用的地方。
在那一行,我们可以得到一些提示,它与 CanActivate
有相同的技巧,因为 CanActivate
的高级调用者 runCanActivate
是之后调用。
第 2 步 - 查看 runCanActivateChild
是如何工作的
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);
}
}
}
有点长。但是如果你仔细研究它,你会发现它在进行深度优先遍历。让我们忽略相同的路由切换。找到#####易云评论#####
,查看主要流程。它表明它首先更新canActivateChecks
,然后执行下一级遍历(整体为前序遍历)。
你必须知道路由器将应用程序的所有路由视为一个 url 树。每个 PreActivation
通过遍历将其 future
(作为树路径)分成路径段。
举一个简化的例子:
we have the future route as
/a/b/c
.
Then we will get [ '/a', '/a/b', '/a/b/c' ] ascanActivateChecks
显然,canActivateChecks
代表了 future
从顶部到最深处的路线
源代码显示 canActivateChecks
从左到右迭代。
第 4 步 - 结论
我们可以得出结论,CanActivateChild
是从顶部到最深的 child 运行的。
希望我解释清楚。
关于Angular 守卫,文档中的陈述不明确,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45005949/