在路由器订阅中调用订阅方法时,我是不是应该取消订阅 ActivatedRoute?

Posted

技术标签:

【中文标题】在路由器订阅中调用订阅方法时,我是不是应该取消订阅 ActivatedRoute?【英文标题】:Should i unsubscribe from ActivatedRoute when calling subscribe method within a Router subscription?在路由器订阅中调用订阅方法时,我是否应该取消订阅 ActivatedRoute? 【发布时间】:2020-10-26 22:39:45 【问题描述】:

我了解到,通常您不必明确取消订阅 RouterActivatedRoute,因为:

ActivatedRoute 及其 observables 与 Router 隔离 本身。当路由组件不再存在时,路由器会销毁它 需要,注入的 ActivatedRoute 也随之消亡。

来源:Do I have to unsubscribe from ActivatedRoute (e.g. params) observables?

但是我在父组件app.component 中反复调用ActivatedRoute.firstChild.paramMap 上的subscribe()。该组件仅在用户离开 Web 应用程序或关闭网站时才会被销毁,所以我担心那些 paramMap 订阅可能会一直保持活动状态。

订阅调用是在 Router.events 订阅中进行的,如下所示:

this.routerSubscription = this.router.events
  .pipe(
    tap((event) =>  
      switch (true) 
        case event instanceof NavigationStart:  
          setTimeout(() => this.showChildComponentLoading = true);
          break;
        

        case event instanceof NavigationEnd:
          this.activatedRoute.firstChild.paramMap.subscribe(paramMap => 
          if(paramMap.has('mode'))
              let newWebAppMode:string = paramMap.get('mode');
              if(this.dataStoreService.isValidWebAppMode(newWebAppMode) && newWebAppMode !== this.dataStoreService.currentAppMode)
                  this.saveToLocalStorage.next([DataType.WEB_APP_MODE, newWebAppMode]); 
          
        );
//other code

每次有人导航到另一个组件/页面时,activatedRoute.firstChild.paramMap 都会再次被订阅。我必须在路由器订阅中执行此操作,并且仅当事件是 NavigationEnd 的实例时才这样做,因为在此之前 url 参数尚不可用。

我的问题是,这些订阅会发生什么?他们是自动退订还是我需要手动退订每个新的?如果是后者,我该如何有效地做到这一点?

你们中的一些人可能会建议我使用activatedRoute.firstChild.snapshot.paramMap.myParameter,因为这样我就不必订阅任何东西了。但是,snapshot 在重复使用相同组件时 url 参数更改时不起作用。所以我不能用那个。

谢谢

【问题讨论】:

当你离开时,一切都被清除了,没有任何东西保持“订阅”,因为整个 js 上下文都被释放了 【参考方案1】:

是的,它看起来确实像潜在的内存泄漏:在每个NavigationEnd 上都会产生一个新订阅,因此您可能有无限数量的相同流的相同观察者(并且activatedRoute.firstChild.paramMap 无限期发出)。您可以通过在订阅中添加一个简单的console.log(new Date()) 来验证这一点。

我相信switchMap 是您的朋友(订阅新流时它会自动退订旧流)。某样东西

this.router.events.pipe(
    filter(event => event instanceof NavigationEnd),
    switchMap(() => this.activatedRoute.firstChild.paramMap),
    filter(paramMap => paramMap.has('mode')),
    map(paramMap => paramMap.get('mode')),
    filter(newMode => this.dataStoreService.isValidWebAppMode(newMode) && 
        newMode !== this.dataStoreService.currentAppMode),
).subscribe(newMode => this.saveToLocalStorage.next([DataType.WEB_APP_MODE, newMode]);

顺便说一句,你不需要防止this.activatedRoute.firstChild 为空吗?

【讨论】:

不,我不必提防这种情况。父组件总是显示一个子组件。我发现我不必取消订阅,因为它在后台重用了相同的 ActivatedRoute 实例。我在添加pipe(first()) 时发现了这一点。 first() 将允许 ActivatedRoute 只发出一个值,然后它会自动完成。这意味着 ActivatedRoute 之后将不再工作。所以最好不要手动退订【参考方案2】:

在这些情况下无需退订

    在 HttpClient 调用的情况下,因为 observable 发出一个值(成功或错误)并自动完成。

    万一或ActivatedRoute订阅因为Router会在组件被销毁时自动销毁

    使用异步管道

【讨论】:

the Router will destroy it when the component 是的,但是在浏览器关闭或用户离开网站之前,app.component 不会被销毁。这意味着这些订阅可以在内存中保持活跃,直到用户停止浏览网站。但我对此不确定,因此提出了这个问题。

以上是关于在路由器订阅中调用订阅方法时,我是不是应该取消订阅 ActivatedRoute?的主要内容,如果未能解决你的问题,请参考以下文章

Pusher 订阅和取消订阅过程

我应该取消订阅根 Angular 组件中的 observables 吗?

我们是不是需要取消订阅 Angular 中的 http 调用? [复制]

在结算周期结束时取消 PayPal 订阅

如何取消订阅路由解析类中的可观察对象

如何取消订阅角度组件中的多个可观察对象?