一、需求
如圖所示,打開多個頁面,切換頁面時能夠保留之前打開的頁面。且適應(yīng)各種懶路由的切換,不單單是組件之間切換。

需求.png
二、廢話不多說,直接上代碼
這是關(guān)鍵文件:app-reuse-strategy.ts
這里我參考的漂_泊的方法,但是并不能實現(xiàn)同一個懶路由下不同路徑下的路由切換, 例如:order/tab1和order/tab2同時存在的情況下,跳轉(zhuǎn)會出現(xiàn)錯誤:Cannot reattach ActivatedRouteSnapshot created from a different route

error.png
然后找到了github上 dmitrimaltsev的解決方法,很好的實現(xiàn)了,感謝大神。
然后就噔噔瞪~~:
import {ActivatedRouteSnapshot, DetachedRouteHandle, RouteReuseStrategy} from '@angular/router';
import {Injectable} from '@angular/core';
interface IRouteConfigData {
reuse: boolean;
}
interface ICachedRoute {
handle: DetachedRouteHandle;
data: IRouteConfigData;
}
@Injectable()
export class AppReuseStrategy implements RouteReuseStrategy {
private static routeCache = new Map<string, ICachedRoute>();
private static waitDelete: string; // 當前頁未進行存儲時需要刪除
private static currentDelete: string; // 當前頁存儲過時需要刪除
/** 進入路由觸發(fā),判斷是否是同一路由 */
shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
return future.routeConfig === curr.routeConfig;
}
/** 表示對所有路由允許復(fù)用 如果你有路由不想利用可以在這加一些業(yè)務(wù)邏輯判斷,這里判斷是否有data數(shù)據(jù)判斷是否復(fù)用 */
shouldDetach(route: ActivatedRouteSnapshot): boolean {
const data = this.getRouteData(route);
if (data) {
return true;
}
return false;
}
/** 當路由離開時會觸發(fā)。按path作為key存儲路由快照&組件當前實例對象 */
store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
const url = this.getFullRouteUrl(route);
const data = this.getRouteData(route);
if (AppReuseStrategy.waitDelete && AppReuseStrategy.waitDelete === url) {
// 如果待刪除是當前路由,且未存儲過則不存儲快照
AppReuseStrategy.waitDelete = null;
return null;
}else {
// 如果待刪除是當前路由,且存儲過則不存儲快照
if (AppReuseStrategy.currentDelete && AppReuseStrategy.currentDelete === url) {
AppReuseStrategy.currentDelete = null;
return null;
}else {
AppReuseStrategy.routeCache.set(url, { handle, data });
this.addRedirectsRecursively(route);
}
}
}
/** 若 path 在緩存中有的都認為允許還原路由 */
shouldAttach(route: ActivatedRouteSnapshot): boolean {
const url = this.getFullRouteUrl(route);
return AppReuseStrategy.routeCache.has(url);
}
/** 從緩存中獲取快照,若無則返回nul */
retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle {
const url = this.getFullRouteUrl(route);
const data = this.getRouteData(route);
return data && AppReuseStrategy.routeCache.has(url)
? AppReuseStrategy.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('/').replace('/', '_');
}
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 {
return route.routeConfig && route.routeConfig.data as IRouteConfigData;
}
/** 用于刪除路由快照*/
public static deleteRouteSnapshot(url: string): void {
if (url[0] === '/') {
url = url.substring(1);
}
url = url.replace('/', '_');
if (AppReuseStrategy.routeCache.has(url)) {
AppReuseStrategy.routeCache.delete(url);
AppReuseStrategy.currentDelete = url;
}else {
AppReuseStrategy.waitDelete = url;
}
}
}
這是重要步驟: 在AppModule中放入,一定放在這個文件下:
providers: [
{ provide: RouteReuseStrategy, useClass: AppReuseStrategy }
]
到這里就把路由復(fù)用部分寫好啦,接下來就是多tab頁面的布局實現(xiàn)。
這里參考的是博客園smiles的文章,以及漂_泊的文章。
我根據(jù)需要修改了一些,在需要的路由下添加data數(shù)據(jù),不需要復(fù)用的不添加:
{ path: '', component: VendorListComponent, data: {title: '商戶列表', module: '/Vendor/List'}}
在home.component.ts中添加刪除
// 關(guān)閉選項標簽
closeUrl(module: string, isSelect: boolean, event: Event) {
event.preventDefault();
// 當前關(guān)閉的是第幾個路由
const index = this.menuList.findIndex(p => p.module === module);
// 如果只有一個不可以關(guān)閉
if (this.menuList.length === 1) return;
this.menuList = this.menuList.filter(p => p.module !== module);
// 刪除復(fù)用
AppReuseStrategy.deleteRouteSnapshot(module);
if (!isSelect) return;
// 顯示上一個選中
let menu = this.menuList[index - 1];
if (!menu) {// 如果上一個沒有下一個選中
menu = this.menuList[index];
}
this.menuList.forEach(p => p.isSelect = p.module === menu.module);
// 顯示當前路由信息
this.router.navigate(['/' + menu.module]);
}
三、參考
1、Github Issues: dmitrimaltsev的解決方法
2、博客園:smiles
3、博客園:漂_泊
4、Angular RouteReuseStrategy