具有 switchMap 重复输出的路由器事件

Router event with switchMap duplicated output

我已经解决了我的问题,但我想了解为什么会这样。

我正在苦苦思索阅读之类的东西,但找不到为什么它会在订阅块上复制输出。

这是代码

的 stackblitz

https://stackblitz.com/edit/angular-szwnz7

我的思路是。

在该组件上,在 NavigationEnd 类型的每个路由器事件上,我想获取部分 ID。

但是我对路由器事件中的任何内容都不感兴趣。 所以我用 activatedRoute.queryParamMap Observable 做了一个 switchMap,在订阅块中获取 ID 号。

每次我在链接之间单击时,它都会显示两个 console.logs。

假设 this.route.queryParamMap 作为 BehaviorSubject,这意味着一旦您订阅它,您就会获得现有值,然后新值会到达。

现在解释一下您的代码,当您单击 link:

时会发生什么
  • this.router.events 发出新的 NavigationEnd 然后你切换到 this.route.queryParamMap
  • this.route.queryParamMap 具有现有值并且该现有值到达订阅
  • this.route.queryParamMap 您现在已经订阅了这个 Observable。由于导航,新的价值也到达这里。 this.router.eventsthis.route.queryParamMap 都是独立的 Observables 并且在导航时都会发出。所以这就像竞争条件。由于 this.router.events 切换到 this.route.queryParamMap 的现有值,您获得第一个值,另一个直接来自 this.route.queryParamMap。这就是另一个值到达订阅的原因。

我认为首先要提到的是 queryParamMap 派生自 queryParams,这是一个 BehaviorSubject:

function createActivatedRoute(c: ActivatedRouteSnapshot) {
  return new ActivatedRoute(
      new BehaviorSubject(c.url), new BehaviorSubject(c.params), new BehaviorSubject(c.queryParams),
      new BehaviorSubject(c.fragment), new BehaviorSubject(c.data), c.outlet, c.component, c);
}

Source

下面是 queryParamMap 的获得方式:

get queryParamMap(): ParamMap {
  if (!this._queryParamMap) {
    this._queryParamMap = convertToParamMap(this.queryParams);
  }
  return this._queryParamMap;
}

Source

在幕后,创建了一个 stream of transitions。因此,当您单击一个按钮时,新转换 将被推送到该流中,这意味着必须激活:

export const activateRoutes =
    (rootContexts: ChildrenOutletContexts, routeReuseStrategy: RouteReuseStrategy,
     forwardEvent: (evt: Event) => void): MonoTypeOperatorFunction<NavigationTransition> =>
        map(t => {
          new ActivateRoutes(
              routeReuseStrategy, t.targetRouterState!, t.currentRouterState, forwardEvent)
              .activate(rootContexts);
          return t;
        });

Source

这将导致调用 advanceActivatedRoute 并最终达到 this line

(<any>route.queryParams).next(nextSnapshot.queryParams);

所以这应该是记录到控制台的第一条消息。

下一个是由于 NavigationEnd event:

(this.events as Subject<Event>)
              .next(new NavigationEnd(
                  t.id, this.serializeUrl(t.extractedUrl), this.serializeUrl(this.currentUrlTree)));

如果你想可视化这个,你可以打开这个StackBlitz,打开开发工具并放置一些断点:

  • router_link.ts: 265 - 当你点击一个按钮时
  • activated_routes.ts: 25 - 当路由被激活时
  • router.ts: 1062 - NavigationEnd 事件
  • router_state.ts: 390 - advanceActivatedRoute的body(在路由激活过程中)
  • app.component.ts