【问题标题】:Router event with switchMap duplicated output带有 switchMap 重复输出的路由器事件
【发布时间】:2020-04-16 00:01:22
【问题描述】:

我已经解决了我的问题,但我想了解为什么会发生这种情况。

我正在拼命阅读和其他东西,但找不到为什么它会复制订阅块上的输出。

这是一个带有代码的堆栈闪电战

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

我的思路是。

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

但是来自路由器的事件内部没有什么让我感兴趣。 所以我用activatedRoute.queryParamMap Observable 做了一个switchMap,在订阅块中获取id 号。

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

【问题讨论】:

    标签: angular rxjs rxjs6


    【解决方案1】:

    我认为首先要提一下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 的主体(在路由激活过程中)
    • app.component.ts

    【讨论】:

      【解决方案2】:

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

      现在解释一下你的代码,当你点击链接时会发生什么:

      • this.router.events 发出新的 NavigationEnd 并且您切换到 this.route.queryParamMap
      • this.route.queryParamMap 具有现有值,并且此现有值到达订阅中
      • this.route.queryParamMap 你现在订阅了这个 Observable。由于导航,新的价值也来到了这里。 this.router.events 和 this.route.queryParamMap 都是独立的 Observable,并且在导航时都会发出。所以这就像比赛条件。由于this.router.events 切换到this.route.queryParamMap 的现有值,而另一个直接来自this.route.queryParamMap,您将获得第一个值。这就是为什么另一个值会到达 subscribe。

      【讨论】:

      • 你的解释让我想到了一些东西,我在 stackblitz 上做了一个修改,它不像 BehaviorSubject 那样有效,如果我错了,请纠正我,但它重复,因为 queryParamMap 订阅运行之前路由改变了,因为查询参数changed然后路由事件触发,并运行新的queryParamMap订阅,我猜对了吗?
      • 我认为你是对的 :D 另外,queryParamMap 确实像 BehaviorSubject 一样工作,在 1 秒内通过setTimeout 订阅它,你会看到它会在订阅时产生价值,即使没有任何反应.但是,您的假设仍然正确。
      猜你喜欢
      • 1970-01-01
      • 2019-08-17
      • 1970-01-01
      • 2017-05-08
      • 1970-01-01
      • 2020-06-21
      • 2021-09-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多