问题描述
我试图深入了解 angular,所以我阅读了文档,这很有帮助.
现在我正在研究守卫.我在文档中阅读了此声明.
I'm trying to understand angular deeply, so i read the docs and it was very helpful.
now i'm studying the guards. and i read this statement in the docs.
路由器首先检查 CanDeactivate 和 CanActivateChild 守卫,从最深的子路由到顶部.然后它会从上到下检查 CanActivate 守卫,直到最深的子路由.
现在我很困惑,为什么 angular 以这种方式执行它?
对 CanDeactivate 进行从最深的孩子到顶部的检查是否有任何好处?CanActivateChild.以及 CanActivate 的从顶部到最深的子路由?
now i'm confused, why does angular perform it in this way?
is there any benefits of doing the checking from the deepest child to the top for CanDeactivate & CanActivateChild. and from top to the deepest child route for CanActivate?
推荐答案
我曾试图相信文档站点中所写的内容.但是,它似乎并不完全正确,或者实现已更新但文档未更新.
I had tried to believe what have written in the docs site. However, it appears it's not totally right, or the implementation has been updated but docs doesn't update.
简明扼要:
首先,CanDeactivate
守卫从最深到顶部检查,CanActivate
守卫从顶部到最深检查(它将退出,并在遍历中进行虚假检查).
First, CanDeactivate
guards are checked from deepest to top and CanActivate
guards are checked from top to deepest(it will quit with falsy check in the traversal).
其次,CanActivateChild
没有从最深到顶部检查守卫.
Second, CanActivateChild
guards are not checked from deepest to top.
TL;博士
我们应该检查源代码,看看它是如何工作的.
we should check the source to see how it work.
注意:检查的提交是:https://github.com/angular/angular/tree/edb8375a5ff15d77709ccf1759efb14091fa86a4
步骤 1 - 查看 CanActivateChild
何时被调用
这只是它的上级调用者 runCanActivateChild
被调用的地方.
This is only place its superior caller runCanActivateChild
got called.
在那一行,我们可以得到一些提示,它与 CanActivate
执行相同的技巧,因为 CanActivate
的上级调用者 runCanActivate
是之后调用.
At that line, we can get some hint that it does the same trick as CanActivate
, because CanActivate
's superior caller runCanActivate
is called after.
L926 和 L950.
runCanActivateChild
在 canActivateChecks
的迭代中被调用,与 runCanActivate
被调用的方式相同.这里我们知道 CanActivate
(我指的是功能)和 CanActivateChild
共享相同的数据源——canActivateChecks
.
runCanActivateChild
got called within the iteration of canActivateChecks
, same as how runCanActivate
got called. Here we know CanActivate
(i mean the feature) and CanActivateChild
share the same data source -- canActivateChecks
.
那么,什么是canActivateChecks
?显然,我们可以发现它是一个 CanActivate
类实例的数组.但是 canActivateChecks
是如何分配的?转到此处 L865.这是重要的部分,所以我要把它们贴在这里.
So, what is canActivateChecks
? Obviously, We can find out it's an array of CanActivate
class instances. But how is canActivateChecks
got assigned? Go to here L865. This is the important part, so i am going to paste them here.
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);
}
}
}
有点长.但是如果你仔细研究它,你会发现它是一个深度优先遍历.让我们忽略相同的路由切换.找到e-cloud #####的#####评论,查看主要流程.它表明它首先更新
canActivateChecks
,然后执行下一级遍历(整个预序遍历).
It's a little long. But If you go through it, you would figure out it plays a depth-first-traversal. Let's ignore the same route switching. Find ##### comment by e-cloud #####
and see the main procedure. It shows that it updates the canActivateChecks
first then performs next level travesal(Pre-order traversal at whole).
你要知道路由器把app的所有路由当成一个url树.每个PreActivation
通过遍历将其future
(作为树路径)分割为路径段.
You must know the router treats all the routes of the app as a url tree. Each PreActivation
split its future
(as a tree path) into path segments by the traversal.
举一个简化的例子:
我们将未来路线设为 /a/b/c
.
然后我们将得到 [ '/a', '/a/b', '/a/b/c' ] 作为 canActivateChecks
显然,canActivateChecks
代表了未来
从顶部到最深处的路线源代码显示 canActivateChecks
从左到右迭代.
Apparently, canActivateChecks
represents the routes from top to deepest of the future
The source shows canActivateChecks
is iterated from left to right.
我们可以得出结论,CanActivateChild
从顶部到最深的孩子运行.
we can conclude that CanActivateChild
is run from top to deepest child.
希望我解释清楚.
这篇关于Angular Guards,文档中的声明不清楚的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!