背景
默认情况下,路由从 A 组件跳转到 B 组件的时候,A 组件的状态也会一并销毁。
但有时候,从 A 组件跳转到 B 组件,再从B 组件切换回 A 组件,我们希望看到A 组件依旧保持原来的状态,表单里面的内容依然存在。
此时,我们就需要 Angular 的路由复用策略,即RouteReuseStrategy
。
RouteReuseStrategy 提供的方法
- shouldDetach:判断是否复用此路由
- store:储存需要复用的路由
- shouldAttach:判断是否启用路由缓存
- retrieve:获取已经储存的路由
- shouldReuseRoute:判断是否应该复用路由
使用方法和步骤
首先,创建一个服务文件: 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),
}
];
注意:RouteReuseStrategy
路由复用的方法,不适用于惰性模块加载。