使用RouteReuseStrategy复用组件时constructor与OnInit钩子是否会触发?
更新
[...]对于懒加载模块,执行shouldAttach方法时route.routeConfig.path的值为空
使用开发者显式自定义的额外数据替代route.routeConfig.path可解决该问题。
原问题
实现RouteReuseStrategy自定义路由复用策略时,观察到如下现象:
- 进入此前已被存储待复用的组件时,
OnInit和constructor会被调用 - 离开此前已被存储待复用的组件时,
OnDestroy不会被调用
从常理来看,后续需要复用的组件不触发OnDestroy是合理的。但被复用的组件仍会调用constructor和OnInit不符合预期,通常我们会在这两个函数中编写初始化逻辑,这会导致每次进入组件都表现为全新实例。
目前网上有大量RouteReuseStrategy相关教程,但未找到相关资料确认上述观察是否正确。
下方是实现的RouteReuseStrategy接口代码,MyComponent被设置为可存储复用,因此离开该组件时不会触发OnDestroy,但重新进入该组件时,它的constructor和OnInit钩子仍会被调用。
import { RouteReuseStrategy, ActivatedRouteSnapshot, DetachedRouteHandle } from '@angular/router'; export class CustomRouteReuseStrategy implements RouteReuseStrategy { private storedRoutes = new Map<string, DetachedRouteHandle>(); /** * 判定当前路由(及其子树)是否需要被分离以便后续复用 * 如果shouldDetach返回true,我们需要存储该路由留待后续复用 * 离开路由时触发 */ shouldDetach(route: ActivatedRouteSnapshot): boolean { if (!route.routeConfig?.path) { return false; } // 阻止MyComponent被销毁(不会触发OnDestroy) return ['my-component'].indexOf(route?.routeConfig?.path) > -1; } /** * 存储被分离的路由,离开路由时触发 */ store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void { if (!route.routeConfig?.path) { return; } const path = this.getPath(route); this.storedRoutes.set(path, handle); } /** * 判定当前路由(及其子树)是否需要被重新附着 * 如果shouldAttach返回true,我们将复用之前存储的路由而非创建新组件 * 进入路由时触发 */ shouldAttach(route: ActivatedRouteSnapshot): boolean { if (!route.routeConfig?.path) { return false; } const path = this.getPath(route); return this.storedRoutes.has(path); } /** * 检索此前存储的路由 */ retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle | null { if (!route.routeConfig?.path) { return null; } const path = this.getPath(route); return this.storedRoutes.get(path) || null; } /** * 判定两个路由是否需要复用 * 每次路由跳转时都会触发 */ shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean { // 新旧路由配置相同时复用路由 return future.routeConfig === curr.routeConfig; } private getPath(route: ActivatedRouteSnapshot): string { if (route.routeConfig !== null && route.routeConfig.path !== null) { return route.routeConfig?.path || ""; } return ""; } }
核心疑问
- 被重新附着(复用)的组件是否应该触发
constructor和OnInit? - 如果上一问题答案为“是”,那只需要在组件首次初始化(非复用场景)执行的代码应该放在哪里?
示例说明
为了还原应用场景,示例的模块层级如下:
- app - home - manager - detail
每个模块都有独立路由配置:
/加载AppComponent/home加载HomeComponent/home/manager加载ManagerComponent/home/detail加载DetailComponent
自定义路由策略会复用ManagerComponent,因此返回该页面时,详情列表不需要重新渲染。
复现步骤
- 访问
/home/manager - 打开控制台查看日志输出
- 点击
/home/detail/1跳转 - 查看控制台,可见
-----> stored route with path "manager"日志输出 - 点击“back to manager”返回,可见控制台输出
ManagerComponent.constructor和ManagerComponent.ngOnInit日志
日志显示路由已被成功附着:
shouldAttach(manager)-----> Yes!retrieve(manager)-----> retrieved route with path "manager"
既然路由已经被成功附着复用,为什么ManagerComponent的constructor和OnInit还会被调用?
内容的提问来源于stack exchange,提问作者Jack
相关产品推荐
相关产品推荐

