Angular 守卫,文档中的陈述不明确

标签 angular angular2-routing guard

我试图深入理解 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会这样执行?
对于 CanDeactivateCanActivateChild,从最深的 child 到顶部进行检查有什么好处。以及从顶部到最深的 CanActivate 子路由?

最佳答案

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

简而言之:

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

其次,CanActivateChild 守卫不会从最深到最顶部进行检查。


长话短说

详细说明

我们应该检查源代码以了解它是如何工作的。

Note: the commit checked is: 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);
      }
    }
  }

有点长。但是如果你仔细研究它,你会发现它在进行深度优先遍历。让我们忽略相同的路由切换。找到#####易云评论#####,查看主要流程。它表明它首先更新canActivateChecks,然后执行下一级遍历(整体为前序遍历)。

你必须知道路由器将应用程序的所有路由视为一个 url 树。每个 PreActivation 通过遍历将其 future (作为树路径)分成路径段

举一个简化的例子:

we have the future route as /a/b/c.
Then we will get [ '/a', '/a/b', '/a/b/c' ] as canActivateChecks

显然,canActivateChecks 代表了 future 从顶部到最深处的路线 源代码显示 canActivateChecks 从左到右迭代。

第 4 步 - 结论

我们可以得出结论,CanActivateChild 是从顶部到最深的 child 运行的。

希望我解释清楚。

关于Angular 守卫,文档中的陈述不明确,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45005949/

相关文章:

javascript - Angular 模块/组件 - 单独显示以及在另一个模块/组件内显示?

angular - 使用 Resolve 停止路由更改

ruby - 使用 Guard 忽略目录

angular - 子组件从 @Input 或服务订阅获取数据?哪个更好?

javascript - ng-for 中的 Angular 增量变量

Angular2 获取当前路径的别名

javascript - Json格式输出

ruby-on-rails - 为什么在dockerizing Rails 应用程序后Guard 没有检测到文件更改?

Haskell - 函数式编程技巧(练习 4.3)

Angular Material 表 Sizing/Scroll