Angular使用路由复用实现单页多窗(Tab)
我们在开发后台管理系统时,一个很重要的需求就是多窗口编辑,来回切换使用。一种思路是使用iframe来记录多个多页面展示,本文通过Angular路由复用来实现这一需求。
路由复用
对于绝大部分应用来说,在切换到某一个路由后,组件初始化后展示,切换到另一个路由后,该组件及其状态和路由状态一并删除,在下一次进入时会重新初始化保证数据不会出错,但在开发后台管理系统时,往往需要多个路由页面来回切换,这时候就需要引入路由复用——RouteReuseStrategy
。
常用方法
Angular
的路由复用主要需要关注的几个方法。
策略方法:
shouldDetach
是否允许路由被复用
shouldAttach
是否允许还原路由
shouldReuseRoute
进入路由判断为同一路由时是否复用路由
使用方法:
store
当路由切换时会触发,缓存上一路由状态
retrieve
获取缓存的路由
下面请看代码
import { RouteReuseStrategy, DefaultUrlSerializer, ActivatedRouteSnapshot, DetachedRouteHandle } from '@angular/router';
import { IdentityService } from './services/identity.service';
export class AppRouteReuseStrategy implements RouteReuseStrategy {
public static handlers: { key: string, value: DetachedRouteHandle }[] = [];
public static close = '';
/** 表示对所有路由允许复用 如果你有路由不想利用可以在这加一些业务逻辑判断 */
public shouldDetach(route: ActivatedRouteSnapshot): boolean {
if (route?.routeConfig?.path === 'path') {
return false;
}
if (route.firstChild) {
// console.log(route?.routeConfig?.path);
return false;
}
return true;
}
/** 当路由离开时会触发。按path作为key存储路由快照&组件当前实例对象 */
public store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
while (route.firstChild) { route = route.firstChild; }
if (route.routeConfig !== null) {
if (!!route.routeConfig.path) {
if (route.children.length === 0) {
let current = '';
route.pathFromRoot.forEach(x => {
if (!!x.routeConfig.path!) {
current += `/${x?.routeConfig?.path}`;
}
});
// console.log(!!AppRouteReuseStrategy.handlers.filter(x => x.key === route.data.module)[0]);
if (!(!!AppRouteReuseStrategy.handlers.filter(x => x.key === route.data.module)[0])) {
if (!!AppRouteReuseStrategy.close && AppRouteReuseStrategy.close === route.data.module) {
return;
}
AppRouteReuseStrategy.handlers.push({ key: route.data.module, value: handle });
}
}
}
}
}
/** 若 path 在缓存中有的都认为允许还原路由 */
public shouldAttach(route: ActivatedRouteSnapshot): boolean {
if (!!route.routeConfig) {
if (!!route.routeConfig.path) {
if (route.children.length === 0) {
let current = '';
route.pathFromRoot.forEach(x => {
if (x?.routeConfig?.path !== undefined && x?.routeConfig?.path !== null) {
current += `/${x?.routeConfig?.path}`;
}
});
return !!route.routeConfig && !!AppRouteReuseStrategy.handlers.filter(x => x.key === route.data.module)[0];
}
return false;
}
else {
return false;
}
}
else { return false; }
}
/** 从缓存中获取快照,若无则返回nul */
public retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle | null {
if (!route.routeConfig) {
return null;
}
if (!!route.routeConfig) {
if (!!route.routeConfig.path) {
if (route.children.length === 0) {
let current = '';
route.pathFromRoot.forEach(x => {
if (x?.routeConfig?.path !== undefined && x?.routeConfig?.path !== null) {
current += `/${x?.routeConfig?.path}`;
}
});
const handle = AppRouteReuseStrategy.handlers.filter(x => x.key === route.data.module)[0];
return !!handle ? handle.value : null;
}
}
}
return null;
}
/** 进入路由触发,判断是否同一路由 */
public shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
return future.routeConfig === curr.routeConfig;
}
}
同时,在AppModule
中的provider将路由复用策略注册进去。
providers: [{ provide: RouteReuseStrategy, useClass: AppRouteReuseStrategy }]
注意: 本文实现的方法是针对不同路由来进行复用,如果需要对同一路由对不同参数进行复用还需有更深入的考虑。