防止Angular在重新访问路由时构建一个组件的新实例(路由重用策略)
防止Angular在重新访问路由时构建一个组件的新实例(路由重用策略)
我将这个GitHub仓库中的应用作为我的angular路由问题的演示应用。应用本身没有问题,因为它非常简单,但是如果您想要,完全可以访问其代码!
我的问题是,每次在这个angular应用程序中导航时,相关组件的一个新实例将被生成。
我在代码中进行了一些更改来说明这种实例化。我添加了一个简单的计数器,如下所示:
export class ContactListComponent implements OnInit { contacts: any[] = []; counter = 0; //<--- counter definition constructor(private contactService: ContactService) { console.log("List Component constructor: #" + ++this.counter); //<--- usage #1 } ngOnInit() { console.log("List Component ngInit: #" + ++this.counter); //<--- usage #2 this.contactService.getContacts().subscribe((data: any[]) => { this.contacts = data; }); } }
如果您看一下以下图表,每次我导航时,都会生成一个新的实例,因此每次计数器都会被重置,在控制台中显示ngInit
和constructor
一遍又一遍:
我甚至尝试了以下代码片段进行导航,但结果是相同的!:
import { Router, ActivatedRoute } from '@angular/router'; constructor( private router: Router ) { } this.router.navigate(['/contacts']);
问题是如何防止这种新的实例化!?
特别是当我们从第二页向后导航回第一页时。
我正在寻找一种像单例那样的技术,只有第一次访问路由时才会实例化组件,当我们再次访问该路由时,我们将得到同一组件的实例。
实际上,这个应用程序不是我正在处理的主要应用程序,我的主要问题是我在其他应用程序中使用了订阅技术
来共享数据,然后当我有几个组件实例时,不同实例中的以下代码将被触发并产生意外结果。为什么呢?因为if (this.agreeTerms)
将在同一时间在不同实例中具有不同的值!
navNext(next) { this.next = next; if (this.next === true && this.router.url.startsWith('/') && this.router.url.endsWith('/')) { this.data.unsetNext(); if (this.agreeTerms) { //<----- this.router.navigate(['/form']); } else { this.error = 'Please accept the terms and conditions'; this.scrollBottom(); } } }
admin 更改状态以发布 2023年5月24日
关于您的评论,@anatoli,使用RouteReuseStrategy将解决此问题。在这里可以看到示例。首先,我们需要创建一个服务类:
RouteReuseService.ts
import { RouteReuseStrategy, ActivatedRouteSnapshot, DetachedRouteHandle, RouterModule, Routes, UrlSegment } from '@angular/router'; export class RouteReuseService implements RouteReuseStrategy { private handlers: { [key: string]: DetachedRouteHandle } = {}; shouldDetach(route: ActivatedRouteSnapshot): boolean { if (!route.routeConfig || route.routeConfig.loadChildren) { return false; } let shouldReuse = false; console.log('checking if this route should be re used or not’, route'); if (route.routeConfig.data) { route.routeConfig.data.reuse ? shouldReuse = true : shouldReuse = false; } return shouldReuse; } store(route: ActivatedRouteSnapshot, handler: DetachedRouteHandle): void { console.log('storing handler'); if (handler) { this.handlers[this.getUrl(route)] = handler; } } shouldAttach(route: ActivatedRouteSnapshot): boolean { console.log('checking if it should be re attached'); return !!this.handlers[this.getUrl(route)]; } retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle { if (!route.routeConfig || route.routeConfig.loadChildren) { return null; }; return this.handlers[this.getUrl(route)]; } shouldReuseRoute(future: ActivatedRouteSnapshot, current: ActivatedRouteSnapshot): boolean { let reUseUrl = false; if (future.routeConfig) { if (future.routeConfig.data) { reUseUrl = future.routeConfig.data.reuse; } } const defaultReuse = (future.routeConfig === current.routeConfig); return reUseUrl || defaultReuse; } getUrl(route: ActivatedRouteSnapshot): string { if (route.routeConfig) { const url = route.routeConfig.path; console.log('returning url', url); return url; } } }
接下来,我们需要在app.module.ts中进行一些更改:
providers: [{provide: RouteReuseStrategy, useClass: RouteReuseService}], bootstrap: [AppComponent]
在最后一步中,我们需要为我们想在应用程序中重用的组件设置“reuse”为true(在app-routing.modules.ts中)。例如,在这里我们想要重用DetailComponent:
const routes: Routes = [{ path: '', component: HomeComponent }, { path: 'detail', component: DetailComponent, data: { reuse: true } }, { path: 'item', component: ItemComponent }];