默认情况下,路由从 A 组件跳转到 B 组件的时候,A 组件的状态也会一并销毁。
但有时候,从 A 组件跳转到 B 组件,再从B 组件切换回 A 组件,我们希望看到A 组件依旧保持原来的状态,表单里面的内容依然存在。
此时,我们就需要 Angular 的路由复用策略,即RouteReuseStrategy
。
首先,创建一个服务文件: route-cache.service.ts
内容如下:
import { Injectable } from '@angular/core'; import { RouteReuseStrategy, DefaultUrlSerializer, ActivatedRouteSnapshot, DetachedRouteHandle } from "@angular/router"; interface IRouteConfigData { reuse: boolean; } interface ICachedRoute { handle: DetachedRouteHandle; data: IRouteConfigData; } @Injectable() export class RouteCacheService implements RouteReuseStrategy { private routeCache = new Map<string, ICachedRoute>(); shouldReuseRoute( future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot ): boolean { let ret = future.routeConfig === curr.routeConfig; console.log("shouldReuseRoute called", ret); if (ret) { this.addRedirectsRecursively(future); // update redirects } return ret; } shouldDetach(route: ActivatedRouteSnapshot): boolean { const data = this.getRouteData(route); console.log( "shouldDetach check if we want to detach and store route", data && data.reuse ); return data && data.reuse; } store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void { const url = this.getFullRouteUrl(route); const data = this.getRouteData(route); this.routeCache.set(url, { handle, data }); this.addRedirectsRecursively(route); //console.log("store route", this.routeCache); } shouldAttach(route: ActivatedRouteSnapshot): boolean { const url = this.getFullRouteUrl(route); console.log( "shouldAttach if retrive route is true", this.routeCache.has(url) ); return this.routeCache.has(url); } retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle | any { const url = this.getFullRouteUrl(route); const data = this.getRouteData(route); console.log( "retrive route", data && data.reuse && this.routeCache.has(url) ); return data && data.reuse && this.routeCache.has(url) ? this.routeCache.get(url)?.handle : null; } private addRedirectsRecursively(route: ActivatedRouteSnapshot): void { const config = route.routeConfig; if (config) { if (!config.loadChildren) { const routeFirstChild = route.firstChild; const routeFirstChildUrl = routeFirstChild ? this.getRouteUrlPaths(routeFirstChild).join("/") : ""; const childConfigs = config.children; if (childConfigs) { const childConfigWithRedirect = childConfigs.find( c => c.path === "" && !!c.redirectTo ); if (childConfigWithRedirect) { childConfigWithRedirect.redirectTo = routeFirstChildUrl; } } } route.children.forEach(childRoute => this.addRedirectsRecursively(childRoute) ); } } private getFullRouteUrl(route: ActivatedRouteSnapshot): string { return this.getFullRouteUrlPaths(route) .filter(Boolean) .join("/"); } private getFullRouteUrlPaths(route: ActivatedRouteSnapshot): string[] { const paths = this.getRouteUrlPaths(route); return route.parent ? [...this.getFullRouteUrlPaths(route.parent), ...paths] : paths; } private getRouteUrlPaths(route: ActivatedRouteSnapshot): string[] { return route.url.map(urlSegment => urlSegment.path); } private getRouteData(route: ActivatedRouteSnapshot): IRouteConfigData | any { return route.routeConfig && (route.routeConfig.data as IRouteConfigData); } }
然后,在根模块里(app.module.ts)引入并注入此服务:
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { AppRoutingModule, Components } from './app-routing.module'; import { AppComponent } from './app.component'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { HttpClientModule } from '@angular/common/http'; import { MaterialModule } from '@common/material/material.module'; import { ApiService } from '@common/service/api/api.service'; import { SnackBarService } from '@common/material/snack-bar/snack-bar.service'; // 引入默认的路由复用接口类 RouteReuseStrategy 和我们自定义的服务 import { RouteReuseStrategy } from "@angular/router"; import { RouteCacheService } from '@common/service/route/route-cache.service'; @NgModule({ declarations: [ AppComponent, ...Components ], imports: [ BrowserModule, AppRoutingModule, BrowserAnimationsModule, HttpClientModule, MaterialModule, ], providers: [ApiService, SnackBarService, // 注入 RouteCacheService 服务 { provide: RouteReuseStrategy, useClass: RouteCacheService }], bootstrap: [AppComponent] }) export class AppModule { }
最后,哪个路由需要复用(根路由和子路由都如此配置),就添加 data: { reuse: true }
即可:
const routes: Routes = [ { path: '', // data: { reuse: true }, component: InPaymentComponent }, { path: 'update', loadChildren: () => import('./modules/update/update.module').then((m) => m.UpdateModule), }, { path: 'check', loadChildren: () => import('./modules/check/check.module').then((m) => m.CheckModule), } ];