Angular 路由复用策略:
对于单页面应用来说,每次路由切换,都会销毁前面的组件,路由离退时组件状态也一并被删除,重新加载相应路由对应的组件,在绝大多数的场景下,这样的处理都是合理的,但是有时候我们也会有一些特殊的需求,需要返回上一个页面时,保留离开页面时的状态。
需求场景应用比如某列表,用户查询了到某条数据后,点击到详情页面,但返回之后想要保持上次列表搜索的状态。比如手机端,浏览滑动到页面某一处之后,点击了详情跳转,滑动返回之前的页面,想要保留到滑动之前的浏览位置状态,这些场景都需要保留组件状态的需求。
之前的路由执行后,会清除所有组件的状态,返回上个页面,就重新在加载组件。为了达成上述保留组件状态的需求,angular推出了路由复用策略。
angular官方文档中提到了 RouteReuseStrategy,提供一种自定义复用已激活路由的方式保存快照。接口声明了5个方法,我们都称它为:路由复用策略,本质是解决不同路由页切换时组件数据不丢失问题。
shouldDetach()
确定是否应分离此路由(及其子树)以便以后复用**
/**
**param: route ActivatedRouteSnapshot 包含与当前组件相关的路由的当前瞬间信息
**/
abstract shouldDetach(route: ActivatedRouteSnapshot): boolean
store()
存储分离的路由。
/**
**param: route ActivatedRouteSnapshot 包含与当前组件相关的路由的当前瞬间信息
**param: handle DetachedRouteHandle 已分离的路由树
**/
abstract store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void
shouldAttach()
确定是否应重新连接此路由(及其子树)
/**
**param: route ActivatedRouteSnapshot 包含与当前组件相关的路由的当前瞬间信息
**/
shouldAttach(route: ActivatedRouteSnapshot): boolean
retrieve()
检索以前存储的路由
/**
**param: route ActivatedRouteSnapshot 包含与当前组件相关的路由的当前瞬间信息
**/
//返回值 DetachedRouteHandle 表示已分离的路由树。
abstract retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle | null
shouldReuseRoute()
确定是否应复用路由 当将来的路由配置和当前的路由配置相同时,此策略将返回 true
/**
**param: future 将要离开的路由 ActivatedRouteSnapshot 包含与当前组件相关的路由的当前瞬间信息
**param: curr 将要加载的路由 ActivatedRouteSnapshot 包含与当前组件相关的路由的当前瞬间信息
**/
//每次切换路由时都会被调用
abstract shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean
这些方法实现路由复用的流程如下图所示
我们要实现路由复用,需要自定义来实现这个类。在切换路由时,若符合路由复用的条件则将路由的快照存放起来,下次再打开此路由时加载对应快照。
1.首先,我们先要新建一个自定义路由复用的ts文件来实现接口RouteReuseStrategy。
import { RouteReuseStrategy, ActivatedRouteSnapshot, DetachedRouteHandle } from '@angular/router';
export class ReuseStrategy implements RouteReuseStrategy {
/*
* @帮助理解
* 假设访问了路由 xxx/a 该路由是设置为允许复用(shouldDetach)
* xxx/a 存入在 store 中
* shouldReuseRoute 命中, 当再次访问 xxx/a 时, 则该路由是要复用的路由
* 判断 shouldAttach 是否还原, 可以从 retrieve 中拿到路由快照, 重新构建渲染组件
*/
private cacheRouters = new Map<string, DetachedRouteHandle>();
// 是否允许复用路由
shouldDetach(route: ActivatedRouteSnapshot): boolean {
return Boolean(route.data.keepalive);
}
// 存入路由(路由离开出发)
store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
const url = this.getFullRouteURL(route);
this.cacheRouters.set(url, handle);
}
// 是否允许还原路由
shouldAttach(route: ActivatedRouteSnapshot): boolean {
const url = this.getFullRouteURL(route);
return Boolean(route.data.keepalive) && this.cacheRouters.has(url);
}
// 获取存储路由
retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle {
const url = this.getFullRouteURL(route);
if (route.data.keepalive && this.cacheRouters.has(url)) {
return this.cacheRouters.get(url);
} else {
return null;
}
}
// 相同路由是否复用(路由进入触发)
shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
return future.routeConfig === curr.routeConfig && JSON.stringify(future.params) === JSON.stringify(curr.params);
}
//删除存储的路由
deleteRouteCache(url: string): void {
if (this.cacheRouters.has(url)) {
const handle: any = this.cacheRouters.get(url);
try {
handle.componentRef.destory();
} catch (e) { throw e }
this.cacheRouters.delete(url);
}
}
//删除存储的全部路由
deleteAllRouteCache(): void {
this.cacheRouters.forEach((handle: any, key: string) => {
this.deleteRouteCache(key);
});
}
//获取完整路由路径
private getFullRouteURL(route: ActivatedRouteSnapshot): string {
const { pathFromRoot } = route;
let fullRouteUrlPath: string[] = [];
pathFromRoot.forEach((item: ActivatedRouteSnapshot) => {
fullRouteUrlPath = fullRouteUrlPath.concat(this.getRouteUrlPath(item));
});
return `/${fullRouteUrlPath.join('/')}`;
}
private getRouteUrlPath(route: ActivatedRouteSnapshot) {
return route.url.map(urlSegment => urlSegment.path);
}
}
2.在app.nodule.ts文件中注册使用ReuseStrategy;
providers: [
...
{ provide: RouteReuseStrategy, useClass: ReuseStrategy }
],
3.在需要使用路由复用的路由配置中做配置keepalive: true,就能实现路由复用的效果了。
const routes: Routes = [ ..., { path: 'test', component: TestComponent, data: { keepalive: true } } ];
直接在路由切换的时候就能控制那些路由需要使用路由复用就行。
实验结果为动态图,超过附件上传的最大限制,所以无法配图,有需要的可以参考uniontech官网源码。
总结
angular RouteReuseStrategy 缓存(路由)组件很精简的实现了状态保留,数据保存的问题,可用于懒加载路由等各种不同的路由实现方式,简单好用,强烈推荐。