用 Angular 路由实现的 Tab 切换,当在不同 Tab 之间切换时,希望保留 Tab 中的状态,这时就需要用到路由复用策略 `RouteReuseStrategy`。用 上一篇 中的例子来继续说明路由复用策略:`Sub1` Tab 下有一个输入框,随便输入一些内容,当从 `Sub2` Tab 切换到 `Sub1` Tab 中时,要保留 `Sub1` Tab 中之前输入的内容。这就是本文要实现的一个效果。
简单来说,就是当离开当前路由的时候,将当前路由的快照存起来,当再次进入到该路由时,再取之前存的快照来恢复当前路由的一个状态。
1. 实现 `RouteReuseStrategy`。
网上很多大神已经实现了,这里我就直接拿过来用了。
// SimpleReuseStrategy.ts
import { RouteReuseStrategy, DefaultUrlSerializer, ActivatedRouteSnapshot, DetachedRouteHandle } from '@angular/router';
export class SimpleReuseStrategy implements RouteReuseStrategy {
public static handlers: { [key: string]: DetachedRouteHandle } = {}
private static waitDelete: string
/** 表示对所有路由允许复用 如果你有路由不想利用可以在这加一些业务逻辑判断 */public shouldDetach(route: ActivatedRouteSnapshot): boolean {console.debug('===shouldDetach-route', route);return true;}
/** 当路由离开时会触发。按path作为key存储路由快照&组件当前实例对象 */public store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {console.debug('===store-route', route, 'store-handle', handle);SimpleReuseStrategy.handlers[this.getRouteUrl(route)] = handle}
/** 若 path 在缓存中有的都认为允许还原路由 */public shouldAttach(route: ActivatedRouteSnapshot): boolean {console.debug('===shouldAttach-route', route);return !!SimpleReuseStrategy.handlers[this.getRouteUrl(route)]}
/** 从缓存中获取快照,若无则返回null */public retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle {console.debug('===retrieve-route', route);if (!route.routeConfig) {return null}
return SimpleReuseStrategy.handlers[this.getRouteUrl(route)]}
/** 进入路由触发,判断是否同一路由 */public shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {console.debug('===shouldReuseRoute-future', future, 'shouldReuseRoute-cur', curr);return future.routeConfig === curr.routeConfig &&JSON.stringify(future.params) == JSON.stringify(curr.params);}
private getRouteUrl(route: ActivatedRouteSnapshot) {var path = route['_routerState'].url.replace(/\//g, '_');console.debug('---getRouteUrl-path', path);return path;}
public static deleteRouteSnapshot(name: string): void {if (SimpleReuseStrategy.handlers[name]) {delete SimpleReuseStrategy.handlers[name];} else {SimpleReuseStrategy.waitDelete = name;}}}
2. 将该策略注入到 AppModule 中。
// app.module.ts
import { BrowserModule } from '@angular/platform-browser';import { NgModule } from '@angular/core';import { AppRoutingModule } from './app-routing.module';import { AppComponent } from './app.component';import { SimpleReuseStrategy } from './route/SimpleReuseStrategy';import { RouteReuseStrategy } from '@angular/router';import { LazyHomeComponent } from './lazy-home/lazy-home.component';
@NgModule({declarations: [AppComponent],imports: [BrowserModule,AppRoutingModule],providers: [{ provide: RouteReuseStrategy, useClass: SimpleReuseStrategy}],bootstrap: [AppComponent]})export class AppModule { }
3. 在 app.component.ts 中添加路由事件。
import { Component } from '@angular/core';import { SimpleReuseStrategy } from './route/SimpleReuseStrategy';import { ActivatedRoute, Router, NavigationEnd } from '@angular/router';import 'rxjs/add/operator/filter';import 'rxjs/add/operator/map';import 'rxjs/add/operator/mergeMap';
@Component({selector: 'app-root',templateUrl: './app.component.html',styleUrls: ['./app.component.css'],providers: [SimpleReuseStrategy]})export class AppComponent {title = 'app';
menuList: Array<{ module: string }>=[];
constructor(private router: Router,private activatedRoute: ActivatedRoute) {//路由事件this.router.events.filter(event => event instanceof NavigationEnd).map(() => this.activatedRoute).map(route => {while (route.firstChild) route = route.firstChild;return route;}).filter(route => route.outlet === 'primary').mergeMap(route => route.data).subscribe((event) => {var menu = { module: event["module"]};this.menuList.push(menu);});}}
4. 路由中 data 的定义。
为各个路由添加 data 的定义。
const routes: Routes = [{path: '',component: LazyHomeComponent,children:[{path:'home-sub1',loadChildren:'./lazy-home-sub1.module#LazyHomeSub1Module',data:{ module:'LazyHomeSub1Module'}},{path:'home-sub2',loadChildren:'./lazy-home-sub2.module#LazyHomeSub2Module',data:{ module:'LazyHomeSub2Module'}}]}];
如果遇到 `Error: Cannot reattach ActivatedRouteSnapshot created from a different route` 这个问题,可以参考 :
https://stackoverflow.com/questions/41584664/error-cannot-reattach-activatedroutesnapshot-created-from-a-different-route
http://www.cnblogs.com/lslgg/p/7700888.html
https://zhuanlan.zhihu.com/p/29823560
http://ng-alain.com/components/reuse-tab
https://angular.io/api/router/RouteReuseStrategy
https://www.cnblogs.com/lovesangel/p/7853364.html
https://zhuanlan.zhihu.com/p/29823560