angular组件生命周期监控

329 阅读1分钟
import { Injector, NgZone } from '@angular/core';
import { filter, tap, map, delay } from 'rxjs/operators';
import { takeUntil, takeWhile, switchMap } from 'rxjs/operators';
import { Subject, Observable, BehaviorSubject } from 'rxjs';
import {
    OnInit, OnChanges,
    AfterViewInit, AfterContentChecked,
    SimpleChange, SimpleChanges, DoCheck,
    AfterContentInit, AfterViewChecked,
    OnDestroy
} from '@angular/core';
export class Iwe7CoreComponent implements
    OnInit, OnChanges,
    AfterViewInit, AfterContentChecked,
    DoCheck, AfterContentInit, AfterViewChecked,
    OnDestroy {
    _cyc: Map<string, Subject<any>> = new Map();
    _zone: NgZone;
    _hasChange: boolean = false;
    constructor(public injector: Injector) {
        this._zone = this.injector.get(NgZone);
    }
    getCyc(name: string): Observable<any> {
        if (!this._cyc.has(name)) {
            this.createCyc(name, name === 'onDestroy');
        }
        if (name === 'onDestroy') {
            return this._cyc.get(name);
        } else if (name === 'ngOnChanges') {
            // 等到ngOnInit之后,执行ngOnChanges
            return this._cyc.get(name).pipe(
                takeUntil(this.getCyc('onDestroy')),
                filter(res => !!res),
                switchMap((changes: SimpleChanges) => {
                    return this.getCyc('ngOnInit').pipe(
                        map(res => changes)
                    );
                })
            );
        } else {
            return this._cyc.get(name).pipe(
                takeUntil(this.getCyc('onDestroy')),
                filter(res => !!res),
                delay(200)
            );
        }
    }
    setCyc(name: string, data: any) {
        if (!this._cyc.has(name)) {
            this.createCyc(name, name === 'onDestroy');
        }
        this._cyc.get(name).next(data);
        if (name === 'onDestroy') {
            this._cyc.get(name).complete();
        }
    }
    createCyc(name: string, isSubject: boolean = false) {
        if (isSubject) {
            this._cyc.set(name, new Subject());
        } else {
            this._cyc.set(name, new BehaviorSubject(false));
        }
    }
    runOutsideAngular(fn: any) {
        this._zone.runOutsideAngular(fn);
    }
    run(fn: any) {
        this._zone.run(fn);
    }
    ngOnChanges(changes: SimpleChanges) {
        this.runOutsideAngular(() => {
            this.setCyc('ngOnChanges', changes);
            this._hasChange = true;
        });
    }
    ngOnInit() {
        this.runOutsideAngular(() => {
            this.setCyc('ngOnInit', this);
            // 模拟没有@Input时触发ngOnChanges
            if (!this._hasChange) {
                this.setCyc('ngOnChanges', this);
            }
        });
    }
    ngDoCheck() {
        this.runOutsideAngular(() => {
            this.setCyc('ngDoCheck', this);
        });
    }
    ngAfterContentInit() {
        this.runOutsideAngular(() => {
            this.setCyc('ngAfterContentInit', this);
        });
    }
    ngAfterContentChecked() {
        this.runOutsideAngular(() => {
            this.setCyc('ngAfterContentChecked', this);
        });
    }
    ngAfterViewInit() {
        this.runOutsideAngular(() => {
            this.setCyc('ngAfterViewInit', this);
        });
    }
    ngAfterViewChecked() {
        this.runOutsideAngular(() => {
            this.setCyc('ngAfterViewChecked', this);
        });
    }
    ngOnDestroy() {
        this.runOutsideAngular(() => {
            this.setCyc('ngOnDestroy', this);
        });
    }
}
  • 案例

等到ngOnInit之后,执行ngOnChanges

export class TestComponent extends Iwe7CoreComponent{
    constructor() {
        super();
        // 等到ngOnInit之后,执行ngOnChanges
        this.getCyc('ngOnChanges').pipe(
            switchMap((changes: SimpleChanges) => {
                return this.getCyc('ngOnInit').pipe(
                    map(res => changes)
                );
            })
        ).subscribe(res => {
            console.log(res);
        });
    }
}
  • 当ngAfterViewInit后执行插入操作

当子组件调用父组件方法时极为有用

attachImHeader(tpl: TemplateRef<any>) {
    this.getCyc('ngAfterViewInit').subscribe(res => {
        this.imHeader.createEmbeddedView(tpl);
    });
}