angular,路由复用策略

1,370 阅读3分钟

RouteReuseStrategy

 abstract  class  RouteReuseStrategy {
   // 判断是否复用路由
   abstract  shouldReuseRoute(future:  ActivatedRouteSnapshot, curr:  ActivatedRouteSnapshot): boolean
   // 存储路由快照&组件当前实例对象
   abstract  store(route:  ActivatedRouteSnapshot, handle:  DetachedRouteHandle):  void
  // 判断是否允许还原路由对象及其子对象
   abstract  shouldAttach(route:  ActivatedRouteSnapshot): boolean
  // 获取实例对象,决定是否实例化还是使用缓存
   abstract  retrieve(route:  ActivatedRouteSnapshot):  DetachedRouteHandle  |  null
   // 判断路由是否允许复用
   abstract  shouldDetach(route:  ActivatedRouteSnapshot): boolean
}

新建文件,block.service.ts

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

@Injectable()
export class BlockService implements RouteReuseStrategy {
  public handlers: { [key: string]: DetachedRouteHandle } = {};

  //表示对路由允许复用
  shouldDetach(route: ActivatedRouteSnapshot): boolean {
    //默认对所有路由复用 可通过给路由配置项增加data: { keep: true }来进行选择性使用,代码如下
    //如果是懒加载路由需要在生命组件的位置进行配置
    if (!route.data.keep) {
      return false;
    }
    return true;
  }

  //当路由离开时会触发。按path作为key存储路由快照&组件当前实例对象
  store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
    const path = route.routeConfig?.path
    if (path) {
      this.handlers[path] = handle;
    }
  }

  //若path在缓存中有的都认为允许还原路由
  shouldAttach(route: ActivatedRouteSnapshot): boolean {
    if(route.routeConfig?.path){
      return !!this.handlers[route.routeConfig?.path]
    }
    return false
  }

  // 从缓存中获取快照,若无则返回null
  retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle| null {
    if (!route.routeConfig) return null;
    //在loadChildren路径上通过修改自定义RouteReuseStrategy中的检索函数时从不检索分离的路由。
    if (route.routeConfig.loadChildren) return null; 
    if(route.routeConfig?.path){
      return this.handlers[route.routeConfig?.path];
    }
    return null
  }

  //进入路由触发,判断是否同一路由
  shouldReuseRoute(future: ActivatedRouteSnapshot, current: ActivatedRouteSnapshot): boolean {
    return future.routeConfig === current.routeConfig;
  }
}

block.service.ts配置,其对应的路由写法

{ 
    path: 'home', 
    component:HomeComponent,
    data:{
        keep:true
    }         
}

但是这里存在问题,加入需要用到路由懒加载,对应子路由path值为空的情况,就无法在store钩子里面存路由快照

// 父路由
{ 
    path: 'home', 
    loadChildren: () => import('@pages/home/home.module').then(m => m.HomeModule)
}
// 子路由
{
    path:"",
    component:HomeComponent,
    data:{
      keep:true
    }
}

对block.service.ts重新加以改造,父路由path与子路由path拼接,拼接成唯一键值,再赋值 新建custom-route-reuse-strategy.service.ts

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

const pathFunc = (currentPath:string | undefined ,parentPath:string | undefined ):string => {
  let path = "";
  if(parentPath !== null && parentPath !== undefined && currentPath !== null && currentPath !== undefined){
    path = parentPath+currentPath;
  }else if(currentPath !== null && currentPath !== undefined){
    path = currentPath;
  }
  return path;
}

interface iCacheRouters {
  snapshot: ActivatedRouteSnapshot
  handle: DetachedRouteHandle
}

@Injectable()
export class CustomRouteReuseStrategyService implements RouteReuseStrategy {

  public cacheRouters:{[key:string]:iCacheRouters} = {};

  //表示对路由允许复用
  shouldDetach(route: ActivatedRouteSnapshot): boolean {
    //默认对所有路由复用 可通过给路由配置项增加data: { keep: true }来进行选择性使用,代码如下
    //如果是懒加载路由需要在生命组件的位置进行配置
    // {path: 'search', component: SearchComponent, data: {keep: true}},
    if (!route.data.keep) {
      return false;
    }else{
      return true;
    }
  }

  //当路由离开时会触发。按path作为key存储路由快照&组件当前实例对象
  store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
    const currentPath = route.routeConfig?.path;
    const parentPath = route.parent?.routeConfig?.path;     
    const path = pathFunc(currentPath,parentPath); 
    if (path) {
      this.cacheRouters[path] = {
        snapshot:route,
        handle:handle
      }
    }    
  }

  //若path在缓存中有的都认为允许还原路由
  shouldAttach(route: ActivatedRouteSnapshot): boolean {
    const currentPath = route.routeConfig?.path;
    const parentPath = route.parent?.routeConfig?.path;     
    const path = pathFunc(currentPath,parentPath);
    if(path){
      return !!this.cacheRouters[path]
    }
    return false
  }

  // 从缓存中获取快照,若无则返回null
  retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle| null {
    const currentPath = route.routeConfig?.path;
    const parentPath = route.parent?.routeConfig?.path;     
    const path = pathFunc(currentPath,parentPath);    
    if (!route.routeConfig) return null;
    if (route.routeConfig.loadChildren) return null; //在loadChildren路径上通过修改自定义RouteReuseStrategy中的检索函数时从不检索分离的路由。
    if(path){
      return this.cacheRouters[path].handle;
    }
    return null
  }

  //进入路由触发,判断是否同一路由
  shouldReuseRoute(future: ActivatedRouteSnapshot, current: ActivatedRouteSnapshot): boolean {
    return future.routeConfig === current.routeConfig;
  }

}

但是根据业务场景需求,退出系统后,换另一个用户登陆,还保存原来的快照状态,那是不合理的,这时候按需订制路由策略

shouldAttach(route: ActivatedRouteSnapshot): boolean {
    const currentPath = route.routeConfig?.path;
    const parentPath = route.parent?.routeConfig?.path;     
    const path = pathFunc(currentPath,parentPath);
    if(path){
      if(currentPath === 'login'){ // 在路由是login的时候清空缓存,login需要特殊判断
        this.cacheRouters = {};
      }
      return !!this.cacheRouters[path]
    }
    return false
}

添加到app.module.ts

@NgModule({
  providers: [
    { 
        provide: RouteReuseStrategy, 
        useClass: CustomRouteReuseStrategyService
    } // 路由复用策略
  ]
})

待续,需要补充,类似管理的多标签效果,单独关闭页面删除快照的方法