找回密码
 立即注册
首页 业界区 业界 笔记 | Angular 实现 keep-alive (路由复用)

笔记 | Angular 实现 keep-alive (路由复用)

陶田田 2025-6-6 16:34:20
Angular 的路由复用策略(RouteReuseStrategy)是一种用于优化路由跳转性能和提高用户体验的机制。通过实现RouteReuseStrategy接口,后可以自定义路由的复用行为,避免不必要的组件销毁和重建,同时保持组件的状态。
以下是对Angular路由复用策略的详细介绍:
一、基本概念

RouteReuseStrategy是 Angular 路由模块提供的一个接口,用于控制路由的复用逻辑。当路由切换时,如果目标路由与当前路由使用相同的组件,通过实现这个接口可以避免组件的重新创建,而是复用现有的组件实例。
二、主要作用


  • 避免不必要的组件销毁和重建:
通过复用组件实例,可以减少 DOM 操作和组件生命周期钩子的调用次数,从而提高应用的性能。

  • 保持组件状态:
在路由切换时,如果组件被复用,那么它的状态(如表单输入、滚动位置等)也会被保留,从而提升用户体验。
三、解决存在的问题(使用场景)

在项目中,我们采用了 Cesium 库来构建并加载三维场景。开发过程中注意到一个用户体验上的显著痛点:每当页面发生跳转并重新返回至 Cesium 场景时,都需要重新进行整个三维场景的加载,这一过程不仅延长了用户的等待时间,降低了整体应用的流畅性,还额外增加了系统资源的消耗,对设备的性能提出了更高要求。
为了解决这个问题,计划采取一种优化策略:将 Cesium 实例化的页面进行 keep-alive 处理。通过这一技术手段,我们可以确保在页面跳转时,Cesium 场景及其加载的所有资源(如地形数据、模型等)能够保持活跃状态,而非被销毁并重新加载。这样,当用户再次访问该页面时,能够立即看到之前已经加载完成的场景,无需经历冗长的加载过程,从而显著提升应用的响应速度和用户体验。
四、实现方法

实现核心类:

实现 RouteReuseStrategy

  • shouldDetach()是否允许复用路由
  • store()当路由离开时会触发,存储路由
  • shouldAttach() 是否允许还原路由
  • retrieve()获取存储路由
  • shouldReuseRoute()进入路由触发,是否同一路由时复用路由
流程:

  • 使用 shouldDetach()把路由 /home 设置为允许复用;
  • 然后通过 store()将路由快照储存起来;
  • 路由切换时会触发 shouldReuseRoute()如果返回为真,(即:再次遇到 /home 路由后表示需要复用路由);
  • 使用 shouldAttach() 检查是否允许还原;
  • 最后通过 retrieve()拿到路由快照并构建组件。
  1. import { Injectable } from '@angular/core';
  2. import { ActivatedRouteSnapshot, DetachedRouteHandle, RouteReuseStrategy } from '@angular/router';
  3. type MyDetachedRouteHandle = DetachedRouteHandle | null;
  4. @Injectable()
  5. export class MyReuseStrategy implements RouteReuseStrategy {
  6.     private static routeCache = new Map<string, DetachedRouteHandle>();
  7.     private static waitDelete: string | null; // 待删除的快照
  8.     /**
  9.      * 用于删除路由快照
  10.      *
  11.      * @param {string} url - 需要删除路由的 URL。
  12.      * @return {void}
  13.      */
  14.     public static deleteRouteSnapshot(url: string): void {
  15.         if (url[0] === '/') {
  16.             url = url.substring(1);
  17.         }
  18.         url = url.replace(/\//g, '_');
  19.         if (MyReuseStrategy.routeCache.has(url)) {
  20.             MyReuseStrategy.routeCache.delete(url);
  21.         }
  22.         MyReuseStrategy.waitDelete = url;
  23.     }
  24.     /**
  25.      * 用于清空路由快照
  26.      */
  27.     public static clearRouteSnapshot(): void {
  28.         MyReuseStrategy.routeCache.clear();
  29.         MyReuseStrategy.waitDelete = null;
  30.     }
  31.     /**
  32.      * 进入路由时触发,根据将来和当前路由配置和参数的比较确定当前路由是否应该重用。
  33.      *
  34.      * @param {ActivatedRouteSnapshot} future - 将来的路由快照。
  35.      * @param {ActivatedRouteSnapshot} curr - 当前路由快照。
  36.      * @return {boolean} 如果应该重用路由,则返回 true,否则返回 false。
  37.      */
  38.     public shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
  39.         return (
  40.             future.routeConfig === curr.routeConfig &&
  41.             JSON.stringify(future.params) === JSON.stringify(curr.params)
  42.         );
  43.     }
  44.     /**
  45.      * 基于路由数据确定路由是否应该分离。
  46.      * 表示对所有路由允许复用 如果有路由不想利用可以在这加一些业务逻辑判断,这里判断路由是否有 keepAlive 数据判断是否复用。
  47.      *
  48.      * @param {ActivatedRouteSnapshot} route - 要检查分离的路由快照。
  49.      * @return {boolean} 如果应该分离路由则返回true,否则返回 false。
  50.      */
  51.     public shouldDetach(route: ActivatedRouteSnapshot): boolean {
  52.         return route.data.keepAlive;
  53.     }
  54.     /**
  55.      * 当路由离开时会触发, 按 path 作为 key 存储路由快照组件当前实例对象
  56.      * 如果路由配置为‘’,则出现Cannot reattach ActivatedRouteSnapshot created from a different route问题
  57.      *
  58.      * @param {ActivatedRouteSnapshot} route - 用于获取完整路由 URL 的路由快照。
  59.      * @param {DetachedRouteHandle} handle - 要存储的路由处理程逻辑。
  60.      * @return {void}
  61.      */
  62.     public store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
  63.         const url = this.getFullRouteUrl(route);
  64.         if (MyReuseStrategy.waitDelete && MyReuseStrategy.waitDelete === url) {
  65.             // 如果待删除是当前路由,且未存储过则不存储快照
  66.             MyReuseStrategy.waitDelete = null;
  67.             return;
  68.         }
  69.         MyReuseStrategy.routeCache.set(url, handle);
  70.     }
  71.     /**
  72.      * 根据 URL 确定是否应该附加路由。
  73.      * 若 URL 在缓存中有的都认为允许还原路由
  74.      *
  75.      * @param {ActivatedRouteSnapshot} route - 要检查的路由快照。
  76.      * @return {boolean} 是否应该附加路由。
  77.      */
  78.     public shouldAttach(route: ActivatedRouteSnapshot): boolean {
  79.         const url = this.getFullRouteUrl(route);
  80.         return MyReuseStrategy.routeCache.has(url);
  81.     }
  82.     /**
  83.      * 从缓存中获取快照,若无则返回 null
  84.      *
  85.      * @param {ActivatedRouteSnapshot} route - 要检查的路由快照
  86.      * @return {MyDetachedRouteHandle} MyDetachedRouteHandle
  87.      */
  88.     public retrieve(route: ActivatedRouteSnapshot): MyDetachedRouteHandle {
  89.         const url = this.getFullRouteUrl(route);
  90.         let handle = MyReuseStrategy.routeCache.has(url)
  91.             ? MyReuseStrategy.routeCache.get(url)
  92.             : null;
  93.         handle = handle ? handle : null;
  94.         return handle;
  95.     }
  96.     /**
  97.      * 基于提供的 ActivatedRouteSnapshot 获取完整的路由 URL。
  98.      *
  99.      * @param {ActivatedRouteSnapshot} route - 用于生成完整路由 URL 的 ActivatedRouteSnapshot
  100.      * @return {string} 过滤、连接和替换字符后的完整路由URL。
  101.      */
  102.     private getFullRouteUrl(route: ActivatedRouteSnapshot): string {
  103.         return this.getFullRouteUrlPaths(route).filter(Boolean).join('/').replace(/\//g, '_');
  104.     }
  105.     /**
  106.      * 基于提供的 ActivatedRouteSnapshot 获取完整的路由 URL 的 paths。
  107.      *
  108.      * @param {ActivatedRouteSnapshot} route - 用于生成完整路由 URL 的 ActivatedRouteSnapshot
  109.      * @return {string []}
  110.      */
  111.     private getFullRouteUrlPaths(route: ActivatedRouteSnapshot): string[] {
  112.         const paths = route.url.map(urlSegment => urlSegment.path);
  113.         return route.parent ? [...this.getFullRouteUrlPaths(route.parent), ...paths] : paths;
  114.     }
  115. }
复制代码
将核心类注册到模块中:

在 Angular 应用中,需要将自定义的路由复用策略注入到应用的根模块(通常是 AppModule )中。这可以通过在 providers 数组中添加一个提供器来实现;
  1. ...
  2. providers: [
  3.         { provide: RouteReuseStrategy, useClass: MyReuseStrategy }
  4. ];
  5. ...
复制代码
路由中开心 keepAlive:
  1. {
  2.   path: 'home',
  3.     ...
  4.   data: { keepAlive: true }
  5. }
复制代码
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

2025-11-13 11:47:12

举报

您需要登录后才可以回帖 登录 | 立即注册