【问题标题】:Angular guards, unclear statement in the docs角度守卫,文档中的声明不明确
【发布时间】:2017-12-13 19:52:28
【问题描述】:

我正在尝试深入了解 Angular,因此我阅读了 the docs,它非常有帮助。
现在我正在研究守卫。我在文档中阅读了这个声明。

路由器首先检查 CanDeactivate 和 CanActivateChild 守卫,从最深的子路由到顶部。然后它从上到下检查 CanActivate 守卫到最深的子路由。

现在我很困惑,为什么 Angular 会以这种方式执行它?
对于 CanDeactivateCanActivateChild 进行从最深的孩子到顶部的检查有什么好处。 CanActivate 的子路径从上到下?

【问题讨论】:

    标签: angular angular2-routing guard


    【解决方案1】:

    我曾试图相信文档网站上所写的内容。但是,它似乎并不完全正确,或者实现已更新但文档没有更新。

    简述:

    首先,CanDeactivate 守卫从从最深到顶部检查,CanActivate 守卫从从顶部到最深检查(它将退出 在遍历中进行虚假检查)。

    其次,CanActivateChild 守卫不检查从深到上


    TL;DR

    详细说明

    我们应该检查源代码,看看它是如何工作的。

    注意:检查的提交是:https://github.com/angular/angular/tree/edb8375a5ff15d77709ccf1759efb14091fa86a4

    第 1 步 - 查看 CanActivateChild 何时被调用

    source here L929.

    这只是它的上级调用者runCanActivateChild 被调用的地方。

    在那一行,我们可以得到一些提示,它与CanActivate 的作用相同,因为CanActivate 的高级调用者runCanActivate 是在之后调用的。

    第 2 步 - 看看 runCanActivateChild 是如何工作的

    L926L950

    runCanActivateChildcanActivateChecks 的迭代中被调用,与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 是从上往下运行的。

    希望我解释清楚。

    【讨论】:

      【解决方案2】:

      当您考虑路由时,您越深入树,就会越具体。

      例如:

      /food-types/sweets/pies/blueberry

      所以当你告诉 Angular 你想要离开 blueberry 饼图时,它会首先检查蓝莓上的 CanDeactivate,因为你正在返回导航树,到达不同的位置。 CanActivateChild 也会沿着树走到子路径,据我了解,出于同样的原因:它想首先检查最深的级别,以验证他们的孩子是否可以被激活。

      CanActivate 的情况正好相反。当你告诉 Angular 你想查看blueberry 饼图时,你正在沿着树走,因此,它会在保护器沿着树走时按顺序检查。

      【讨论】:

      • 很好的解释,但是CanActivateChild呢,它遵循哪种解释?
      • 已更新 - 我不确定为什么这是一种模式,但我已尽力解释它。
      猜你喜欢
      • 1970-01-01
      • 2021-01-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多