Angular路由复用实例

关于在tabset中动态加载Component ,点击查看我之前的文章
介绍了如何使用动态加载Component 的方式实现tabset 标签页,从而达到页面切换。
这种方式缺点就是无法使用Angular 的路由来跳转,多个页面如果如果某个元素的id 值相同会有影响。
以下的方式主要使用了Angular 的路由复用功能—>RouteReuseStrategy

动态加载

主要借鉴了 ng-alian里面的delon
https://github.com/ng-alain/delon
Angular在官网上显示是一个实验性的功能

官网描述
解释
解释
具体来说就是重写上述的5个方法
主要代码
route-reuse-strategy.ts

export class CustomeRouteReuseStrategy implements RouteReuseStrategy {

    constructor(private reuseTabService: ReuseTabService) { }

    /** 表示对所有路由允许复用 如果你有路由不想利用可以在这加一些业务逻辑判断 */
    public shouldDetach(route: ActivatedRouteSnapshot): boolean {
        return this.reuseTabService.shouldDetach(route);
    }
    /** 当路由离开时会触发。按path作为key存储路由快照&组件当前实例对象 */
    public store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
        return this.reuseTabService.store(route, handle);
    }
    /** 若 path 在缓存中有的都认为允许还原路由 */
    public shouldAttach(route: ActivatedRouteSnapshot): boolean {
        return this.reuseTabService.shouldAttach(route);
    }
    /** 从缓存中获取快照,若无则返回null */
    public retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle {
        return this.reuseTabService.retrieve(route);
    }
    /** 进入路由触发,判断是否同一路由 */
    public shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
        return this.reuseTabService.shouldReuseRoute(future,curr);
    }
}

reuse-tab-service.ts

import { Injectable, OnDestroy, Injector } from '@angular/core';
import { ActivatedRouteSnapshot, ActivatedRoute } from '@angular/router';
import { ReuseTabCached, ReuseTabNotify } from '../models/reuse-tab';
import { BehaviorSubject, Observable } from 'rxjs';
import { filter } from 'rxjs/operators';

@Injectable()
export class ReuseTabService implements OnDestroy {
    private _cachedChange: BehaviorSubject<ReuseTabNotify> = new BehaviorSubject<ReuseTabNotify>(null);
    /**缓存路由列表 */
    private _reuseTabCachedList: ReuseTabCached[] = [];
    private _titleCached: { [url: string]: string } = {};
    private removeUrlBuffer: string;
    /** 当前路由地址 */
    get curUrl() {
        return this.getUrl(this.injector.get(ActivatedRoute).snapshot);
    }
    /** 获取已缓存的路由 */
    get reuseTabList(): ReuseTabCached[] {
        return this._reuseTabCachedList;
    }
    /** 获取当前缓存的路由总数 */
    get count() {
        return this._reuseTabCachedList.length;
    }
    /** 订阅缓存变更通知 */
    get change(): Observable<ReuseTabNotify> {
        return this._cachedChange.asObservable()
            .pipe(filter(w => w !== null));
    }
    constructor(private injector: Injector) { }


    /**
     * 关闭tab
     * @param url 路由url
     * @param includeNonCloseable 是否包含强制不可关闭
     */
    close(url: string, includeNonCloseable = false): any {
        this.removeUrlBuffer = url;
        this.remove(url, includeNonCloseable);
        this._cachedChange.next({ active: 'close', url, list: this._reuseTabCachedList });
        this.di('close tag', url);
        return true;
    }

    /**
     * 决定是否允许路由复用,若 `true` 会触发 `store`
     */
    shouldDetach(route: ActivatedRouteSnapshot): boolean {
        if (this.hasInValidRoute(route)) return false;
        this.di('#shouldDetach', this.can(route), this.getUrl(route));
        return this.can(route);
    }
    /**
     * 存储快照
     */
    store(_snapshot: ActivatedRouteSnapshot, _handle: any) {
        // if (this.count >= this._max) this._reuseTabCachedList.shift();
        const url = this.getUrl(_snapshot);
        const index = this.index(url);
        const title = this.getTitle(url, _snapshot);
        const item: ReuseTabCached = {
            title: title,
            closable: true,
            url: url,
            _snapshot: _snapshot,
            _handle: _handle,
        };
        if (index === -1) {
            this._reuseTabCachedList.push(item);
        } else {
            this._reuseTabCachedList[index] = item;
        }
        this.removeUrlBuffer = null;
        this.di('#store', index === -1 ? '[new]' : '[override]', url);
        // if (_handle && _handle.componentRef) {
        //     this.runHook('_onReuseDestroy', url, _handle.componentRef);
        // }
        this._cachedChange.next({ active: 'add', item, list: this._reuseTabCachedList });
    }
    /**
     * 决定是否允许应用缓存数据
     */
    shouldAttach(route: ActivatedRouteSnapshot): boolean {
        if (this.hasInValidRoute(route)) return false;
        const url = this.getUrl(route);
        const data = this.getReuseTabCached(url);
        const ret = !!(data && data._handle);
        this.di('#shouldAttach', ret, url);
        // if (ret && data._handle.componentRef) {
        //     this.runHook('_onReuseInit', url, data._handle.componentRef);
        // }
        return ret;
    }
    /**
     * 提取复用数据
     */
    retrieve(route: ActivatedRouteSnapshot): {} {
        let hasInValidRoute = this.hasInValidRoute(route);
        if (hasInValidRoute) return null;
        const url = this.getUrl(route);
        const data = this.getReuseTabCached(url);
        const ret = (data && data._handle) || null;
        this.di('#retrieve', url, ret);
        return ret;
    }
    /**
     * 决定是否应该进行复用路由处理
     */
    shouldReuseRoute(
        future: ActivatedRouteSnapshot,
        curr: ActivatedRouteSnapshot,
    ): boolean {
        let ret = future.routeConfig === curr.routeConfig;
        if (!ret) return false;
        const path = ((future.routeConfig && future.routeConfig.path) ||
            '') as string;
        if (path.length > 0 && ~path.indexOf(':')) {
            const futureUrl = this.getUrl(future);
            const currUrl = this.getUrl(curr);
            ret = futureUrl === currUrl;
        }
        this.di('=====================');
        this.di('#shouldReuseRoute', ret, `${this.getUrl(curr)}=>${this.getUrl(future)}`, future, curr);
        return ret;
    }

    /** 获取指定路径缓存所在位置,`-1` 表示无缓存 */
    index(url: string): number {
        return this._reuseTabCachedList.findIndex(w => w.url === url);
    }
    /** 获取指定路径缓存是否存在 */
    exists(url: string): boolean {
        return this.index(url) !== -1;
    }
    /** 获取指定路径缓存 */
    getReuseTabCached(url: string): ReuseTabCached {
        return url ? this._reuseTabCachedList.find(w => w.url === url) || null : null;
    }
    /**
     * 获取标题
     * @param url 指定URL
     * @param route 指定路由快照
     */
    getTitle(url: string, route?: ActivatedRouteSnapshot): string {
        if (this._titleCached[url]) return this._titleCached[url];
        if (route && route.data && route.data.title)
            return route.data.title;
    }
    /**
     * 清除标题缓存
     */
    clearTitleCached() {
        this._titleCached = {};
    }
    getTruthRoute(route: ActivatedRouteSnapshot) {
        let next = route;
        while (next.firstChild) next = next.firstChild;
        return next;
    }
    /**
     * 根据快照获取URL地址
     */
    getUrl(route: ActivatedRouteSnapshot): string {
        let next = this.getTruthRoute(route);
        const segments = [];
        while (next) {
            segments.push(next.url.join('/'));
            next = next.parent;
        }
        const url =
            '/' +
            segments
                .filter(i => i)
                .reverse()
                .join('/');
        return url;
    }


    /**
     * 运行生命周期钩子
     * @param method 
     * @param url 
     * @param comp 
     */
    private runHook(method: string, url: string, comp: any) {
        if (comp.instance && typeof comp.instance[method] === 'function')
            comp.instance[method]();
    }

    /**
     * 组件销毁
     * @param _handle 
     */
    private destroy(_handle: any) {
        if (_handle && _handle.componentRef && _handle.componentRef.destroy)
            _handle.componentRef.destroy();
    }
    /**
     * 移除url
     * @param url 
     * @param includeNonCloseable 
     */
    private remove(url: string | number, includeNonCloseable: boolean): boolean {
        const idx = typeof url === 'string' ? this.index(url) : url;
        const item = idx !== -1 ? this._reuseTabCachedList[idx] : null;
        if (!item || (!includeNonCloseable && !item.closable)) return false;

        this.destroy(item._handle);

        this._reuseTabCachedList.splice(idx, 1);
        delete this._titleCached[url];
        return true;
    }

    /**
     * 去掉loadChildren,以及children
     * 得到纯路由
     * @param route 
     */
    private hasInValidRoute(route: ActivatedRouteSnapshot) {
        return (
            !route.routeConfig ||
            route.routeConfig.loadChildren ||
            route.routeConfig.children
        );
    }
    /**
     * 检查快照是否允许被复用
     */
    private can(route: ActivatedRouteSnapshot): boolean {
        const url = this.getUrl(route);
        if (url === this.removeUrlBuffer) return false;
        if (route.data && typeof route.data.reuse === 'boolean')
            return route.data.reuse;
        return true;
    }
    private di(...args) {
        // tslint:disable-next-line:no-console
        console.warn(...args);
    }
    ngOnDestroy(): void {
        this._reuseTabCachedList = [];
        this._cachedChange.unsubscribe();
    }
}

app.module.ts

....
  providers: [
    ReuseTabService,
    {
      provide: RouteReuseStrategy,
      useClass: CustomeRouteReuseStrategy,
      deps: [ReuseTabService]
    }
  ]
 .....
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值