单页面应用现在是主流,随之而来的缺点:页面间切换时不能保存状态
angular4出了一个RouteReuseStrategy路由重用策略可以让组件所有的state和渲染好的html存起来,然后在切回去的时候再调出来。
export declare abstract class RouteReuseStrategy{ abstract shouldReuseRoute():Boolean abstract shouldAttach():Boolean abstract retrieve():DetachedRouteHandle | null abstract shouldDetach():Boolean abstract store():void }
替换一旦发生,某些组件将被丢弃
这时shouldDetech,store将被调用,用于缓存即将被丢弃的组件
有组件丢弃,自然有组件替补进来
这时shouldAttach retreive将被调用,用于调出缓存的组件
🌊 应用场景
1.进入某个路由页面,进行操作,切换页面再返回,能还原到之前的操作状态;
2.切换右边的tab,能进入到对应的路由页面(操作后的状态);
3.点击右边tab里的关闭按钮或页面上的关闭按钮,删除某个路由页面的快照,再次点开这个路由页面,页面恢复初始状态;
🌊 下面是具体实现
要注意的点:
1. store方法:存储上一个页面的快照
2.关闭路由有几种情况:
i 打开A页面后 打开B页面 关闭B页面(此时B页面没有快照) -> 触发store方法
ii 打开A页面后 打开B页面 关闭A页面 (此时A页面有快照) -> 不触发store方法
iii 打开A页面后 打开B页面 回到A页面 关闭A页面 (此时A页面有快照) ->不触发store方法
iii 打开A页面后 打开B页面 回到A页面 关闭B页面 (此时B页面有快照) ->不触发store方法
因此页面在没有快照的时候关闭 需要在store方法里做一个暂缓删除的操作(先记录key 进去新页面后 再删除key)
这样页面的ngOnDestroy()方法才能执行 才能避免事件重复监听(在ngOnDestroy()中执行)
相关的知识点:
ActivatedRouteSnapshot:包含当前插座中加载组件某一特定时间路由信息
RouteReuseStrategy:路由复用策略
DetachedRouteHandle:组件当前所有状态(路由快照
import {ActivatedRouteSnapshot, RouteReuseStrategy, DetachedRouteHandle} from '@angular/router; export class ExtensionRouteReuseStrategy implements RouteReuseStrategy { private static waitDelete:string; public static storedRoutes: Map<string, DetachedRouteHandle> = new Map<string, DetachedRouteHandle>(); constructor(){ //关闭操作(右上角或者history页面) this.storageNotifyRouter.destoryTopic.subscribe((key) => { this.processDestory(key); }); }; //是否缓存【离开路由时触发】 shouldDetach(route: ActivatedRouteSnapshot): boolean { if (route.data && route.data.key) { return true; } return false; } //缓存组件【离开路由时触发】 store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void { const key = this.findStoreKey(route); const key$$ = this.lastClosedKey + _SPLIT; //未存储快照的情况下 跳转新页面 再删key if (this.waitDeleteRoutes.size > 0) { const array = Array.from(this.waitDeleteRoutes.keys()); for (const deletekey of array) { if (deletekey == key) { const oldHandle = handle as { componentRef: ComponentRef<any> }; oldHandle.componentRef.destroy(); } this.waitDeleteRoutes.delete(deletekey); } return; } //存储上一个页面快照 进入新页面 if (handle && !(key === this.lastClosedKey || key.startsWith(key$$))) { this.lastClosedKey = ''; this.storedRoutes.set(key, handle); } } //是否还原【进入路由时触发】 shouldAttach(route: ActivatedRouteSnapshot): boolean { const key = this.findRetrieveKey(route); const result: boolean = !!key && !!this.storedRoutes.get(key); return result; } //还原路由【进入路由时触发】 retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle { const key = this.findRetrieveKey(route); let handle = null; if (key && this.storedRoutes.get(key)) { handle = this.storedRoutes.get(key); this.routerNotifyStorage.notifyRetrieve(key); } return handle; } //是否复用路由【进入路由时触发】 shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean { const result: boolean = future.routeConfig === curr.routeConfig; return result; } findStoreKey(route: ActivatedRouteSnapshot): string { let key = ""; if (route.data && route.data.key) { if (route.children[0] && route.children[0].data && route.children[0].data.key) { this.findStoreKey(route.children[0]); } else { key = route.data.key; } } return key; } findRetrieveKey(route: ActivatedRouteSnapshot): string { let key = ""; if (route.data && route.data.key) { key = route.data.key; } return key; } processDestory(key: string, waiteDelete: boolean = true) { this.lastClosedKey = key; let existKey: boolean = false; const array: string[] = Array.from(this.storedRoutes.keys()); for (const keyStored of array) { const key$$ = key + _SPLIT; //存储快照的情况下 删除快照 if (key === keyStored || keyStored.startsWith(key$$)) { const oldHandle = this.storedRoutes.get(keyStored) as { componentRef: ComponentRef<any> }; oldHandle.componentRef.destroy(); this.storedRoutes.delete(keyStored); existKey = true; } } //未存储快照 先存当前页面key if (!existKey && waiteDelete) { this.waitDeleteRoutes.add(key); } } }
🌊 后续
保存快照
在离开路由时保存上一个页面快照,并emit一个事件给左边的菜单组件和右边的tab组件,告诉它们路由路径已改变,同时把新的路由路径作为参数传过去
然后在左边的菜单组件和右边的tab组件里subscribe事件里去监听变化,替换新的路由路径。
这样在点击它们的时候就会重定向到新的路由路径,而快照已保存。
删除快照
在点击右边tab组件里的关闭按钮或者点击页面上的关闭按钮时,emit一个删除快照的事件,同时把路由路径作为参数传过去
然后在路由复用策略的ts文件中的subscribe事件里去监听这个事件,然后执行对应路由的processDestroy()方法