一、为什么需要路由复用策略

在开发单页应用(SPA)时,我们经常会遇到这样的场景:用户在某个页面填写了一堆表单数据,然后点击导航到另一个页面查看信息,再返回时发现之前填的数据全没了。这种情况会让用户体验大打折扣,尤其是在复杂的业务场景中,比如电商网站的商品筛选、后台管理系统的多标签页等。

Angular 的路由机制默认会在离开某个路由时销毁对应的组件,再重新创建。虽然这样可以保证每次进入的都是“全新”的组件,但同时也意味着组件的状态无法保留。这时候,路由复用策略(RouteReuseStrategy)就派上用场了。

二、Angular 路由复用策略的基本原理

Angular 提供了一个 RouteReuseStrategy 抽象类,我们可以通过继承它并实现几个关键方法来控制路由的复用行为。主要涉及以下几个方法:

  1. shouldReuseRoute:决定是否复用当前路由。
  2. shouldDetach:决定是否在离开路由时保存组件状态。
  3. store:保存组件状态。
  4. shouldAttach:决定是否在返回路由时恢复组件状态。
  5. retrieve:恢复组件状态。

通过合理实现这些方法,我们可以让 Angular 在路由切换时保留组件的状态,而不是每次都重新创建。

三、实现一个简单的路由复用策略

下面我们用一个完整的示例来演示如何实现一个自定义的路由复用策略。

技术栈:Angular 16

1. 创建自定义路由复用策略

import { RouteReuseStrategy, ActivatedRouteSnapshot, DetachedRouteHandle } from '@angular/router';

export class CustomRouteReuseStrategy implements RouteReuseStrategy {
  // 存储已分离的路由句柄
  private storedRoutes = new Map<string, DetachedRouteHandle>();

  // 判断是否复用路由(通常复用同一层级的相同路由)
  shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
    return future.routeConfig === curr.routeConfig;
  }

  // 判断是否在离开路由时保存组件状态
  shouldDetach(route: ActivatedRouteSnapshot): boolean {
    // 这里我们假设只有带有 `data: { reuse: true }` 的路由才需要复用
    return route.data['reuse'] === true;
  }

  // 保存组件状态
  store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
    // 使用路由的 path 作为 key 存储
    const key = this.getRouteKey(route);
    this.storedRoutes.set(key, handle);
  }

  // 判断是否在返回路由时恢复组件状态
  shouldAttach(route: ActivatedRouteSnapshot): boolean {
    const key = this.getRouteKey(route);
    return this.storedRoutes.has(key);
  }

  // 恢复组件状态
  retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle | null {
    const key = this.getRouteKey(route);
    return this.storedRoutes.get(key) || null;
  }

  // 生成路由的唯一标识(这里简单使用 path)
  private getRouteKey(route: ActivatedRouteSnapshot): string {
    return route.routeConfig?.path || '';
  }
}

2. 在 AppModule 中注册策略

import { NgModule } from '@angular/core';
import { RouteReuseStrategy } from '@angular/router';
import { CustomRouteReuseStrategy } from './custom-route-reuse-strategy';

@NgModule({
  providers: [
    { provide: RouteReuseStrategy, useClass: CustomRouteReuseStrategy }
  ]
})
export class AppModule {}

3. 在路由配置中标记需要复用的路由

const routes: Routes = [
  {
    path: 'form',
    component: FormComponent,
    data: { reuse: true }  // 标记该路由需要复用
  },
  {
    path: 'details',
    component: DetailsComponent
  }
];

示例解析

  1. shouldReuseRoute:默认情况下,复用同一层级的相同路由(比如 /form 跳转到 /form)。
  2. shouldDetach:只有配置了 data: { reuse: true } 的路由才会保存状态。
  3. store:将组件的状态存储到 Map 中,以便后续恢复。
  4. shouldAttach:检查当前路由是否有已存储的状态。
  5. retrieve:从缓存中恢复组件状态。

四、进阶:动态控制路由复用

有时候,我们可能希望更灵活地控制哪些路由需要复用。比如,某些路由只在特定条件下才复用。我们可以通过扩展 CustomRouteReuseStrategy 来实现。

示例:根据条件动态决定是否复用

shouldDetach(route: ActivatedRouteSnapshot): boolean {
  // 只有满足特定条件时才复用(比如用户角色为管理员)
  const user = this.authService.currentUser;
  return route.data['reuse'] && user?.role === 'admin';
}

五、应用场景与注意事项

1. 典型应用场景

  • 表单页面:用户在填写表单后跳转查看其他信息,返回时表单数据不丢失。
  • 多标签页系统:类似浏览器标签页,切换标签时保留每个页面的状态。
  • 数据筛选页面:保留用户的筛选条件,避免重复操作。

2. 技术优缺点

优点

  • 提升用户体验,避免重复操作。
  • 减少不必要的组件重建,提高性能。

缺点

  • 内存占用增加,尤其是缓存了大量组件时。
  • 需要手动管理缓存,避免内存泄漏。

3. 注意事项

  • 内存管理:长时间运行的 SPA 需要注意清理不再需要的缓存。
  • 动态内容:如果组件内容依赖实时数据(比如股票行情),复用可能导致数据过时。
  • 路由参数变化:如果路由参数影响组件内容,需要额外处理(比如监听参数变化)。

六、总结

路由复用策略是 Angular 中一个非常强大的功能,能够显著提升复杂场景下的用户体验。通过自定义 RouteReuseStrategy,我们可以灵活控制组件的生命周期,实现状态保留。不过,使用时需要注意内存管理和动态内容的处理,避免引入潜在问题。