天天看點

angular中元件changeDetection為ChangeDetectionStrategy.OnPush時的學習

注:我用的angular 8

一個angular應用是由元件樹組成的,changeDetection是其中比較深的部分,我也不懂哈。

angular中changeDetection中的政策有這樣的描述:

總而言之,對于一個元件而言,2中changeDetection政策,預設的沒啥好說的,主要說一下OnPush的情況。

如果子元件的屬性的變化由輸入屬性決定,那麼這個時候就可以啟用OnPush這種變更檢測政策,這樣輸入屬性不變的時候就不用檢測了,省時省力。

1、 輸入屬性為非對象的時候

index元件:

@Component({
  selector: 'app-index',
  template: '<app-abc [shuru]="shuru"></app-abc>',
})
export class IndexComponent {
  shuru = 1;
  constructor() {
    setInterval(() => {
      this.shuru++;
    }, 1111);
  }
}
           

abc元件:

@Component({
  selector: 'app-abc',
  template: '  輸入是:{{shuru}}',
  changeDetection: ChangeDetectionStrategy.OnPush
})

export class AbcComponent implements OnChanges {
  @Input()
  shuru = 0;
  ngOnChanges(changes: SimpleChanges): void {
    for (let propName in changes) {
      let chng = changes[propName];
      let cur = JSON.stringify(chng.currentValue);
      let prev = JSON.stringify(chng.previousValue);
      console.log(`${propName}: currentValue = ${cur}, previousValue = ${prev}`);
    }
  }
}
           

跑一下看結果:

angular中元件changeDetection為ChangeDetectionStrategy.OnPush時的學習

這個實在沒啥好說的哈!

2、輸入屬性是一個對象的時候

修改index元件代碼如下:

@Component({
  selector: 'app-index',
  template: '<app-abc [shuru]="shuru"></app-abc>',
})
export class IndexComponent {
  shuru = {
    shuru: 1
  };
  constructor() {
    setInterval(() => {
      this.shuru.shuru++;
    }, 1111);
  }
}
           

修改abc元件如下:

@Component({
  selector: 'app-abc',
  template: '  輸入是:{{shuru.shuru}}',
  changeDetection: ChangeDetectionStrategy.OnPush
})

export class AbcComponent implements OnChanges {
  @Input()
  shuru = {shuru: 0};
  ngOnChanges(changes: SimpleChanges): void {
    for (let propName in changes) {
      let chng = changes[propName];
      let cur = JSON.stringify(chng.currentValue);
      let prev = JSON.stringify(chng.previousValue);
      console.log(`${propName}: currentValue = ${cur}, previousValue = ${prev}`);
    }
  }
}
           

再跑一下:

angular中元件changeDetection為ChangeDetectionStrategy.OnPush時的學習

為什麼abc元件裡面沒變化呢,因為再index元件中輸入屬性shuru沒有變化,我們改變的隻是shuru.shuru,angular比較的是shuru的reference

3、輸入屬性與immutable objects

2沒有生效的,是因為我修改的是shuru這個對象,而不是shuru的引用;那麼作為輸入屬性傳入的每個對象本身如果是不可修改的,如果我想修改shuru的時候,重新指派成另外一個對象就可以了。

對于2中的情況,如果我們想讓他生效,這麼做就好了:

index元件稍微修改下:

this.shuru = {
        shuru : ++this.shuru.shuru
      };
      // this.shuru.shuru++;
           
angular中元件changeDetection為ChangeDetectionStrategy.OnPush時的學習

4、OnPush與事件

除了上述3能捕獲到變更檢測,還有一種詭異的情況:

index元件的代碼和2保持一直,修改abc元件的代碼如下:

@Component({
 selector: 'app-abc',
 template: ' 輸入是:{{shuru.shuru}}  <button (click)="click()">我是按鈕</button>',
 changeDetection: ChangeDetectionStrategy.OnPush
})

export class AbcComponent implements OnChanges {
 @Input()
 shuru = {shuru: 0};
 // shuru: Observable<any>;

 ngOnChanges(changes: SimpleChanges): void {
   for (let propName in changes) {
     let chng = changes[propName];
     let cur = JSON.stringify(chng.currentValue);
     let prev = JSON.stringify(chng.previousValue);
     console.log(`${propName}: currentValue = ${cur}, previousValue = ${prev}`);
   }
 }
 click() {
   console.log('電力');
 }
}
           

跑一下:

angular中元件changeDetection為ChangeDetectionStrategy.OnPush時的學習

對此,我隻能解釋成,事件的觸發,導緻元件進行了變更檢測;那麼,我也可以自己在元件abc中手動做變更檢測,是以可以像下面這樣做:

5、OnPush與ngDoCheck、ChangeDetectorRef、markForCheck

When should you use ngDoCheck?

Use ngDoCheck when you want to capture changes that Angular otherwise doesn’t.

For example, if a binding references remains unchanged after a click event, ngOnChanges won’t run but ngDoCheck will.

在2的基礎之上進行。。。

雖然這時候ngOnChanges不執行了,但是ngDoCheck執行啊!!!

修改元件abc的代碼如下:

@Component({
  selector: 'app-abc',
  template: '  輸入是:{{shuru.shuru}}',
  changeDetection: ChangeDetectionStrategy.OnPush
})


export class AbcComponent implements OnChanges, DoCheck {
  @Input()
  shuru = {shuru: 0};

  constructor(private changeDetectorRef: ChangeDetectorRef,
  ) {
    /*
    Detaches this view from the change-detection tree.
    A detached view is not checked until it is reattached.
    Use in combination with detectChanges() to implement local change detection checks.
    */
    this.changeDetectorRef.detach(); // 如果detach, 那麼markForCheck就不起作用了
  }

  ngOnChanges(changes: SimpleChanges): void {
    for (let propName in changes) {
      let chng = changes[propName];
      let cur = JSON.stringify(chng.currentValue);
      let prev = JSON.stringify(chng.previousValue);
      console.log(`${propName}: currentValue = ${cur}, previousValue = ${prev}`);
    }
  }

  ngDoCheck() {
    console.log('ngDoCheck', this.shuru);
    // this.changeDetectorRef.markForCheck();  // 不detach的時候,這個也可以
    this.changeDetectorRef.detectChanges(); // Checks this view and its children.
  }
}
           
angular中元件changeDetection為ChangeDetectionStrategy.OnPush時的學習

6、當輸入屬性是service時候

居然還能這樣!

先搞個服務處理:

@Injectable({
  providedIn: 'root'
})
export class ObsService {
  private messageSource = new BehaviorSubject(1);
  comeOneData = this.messageSource.asObservable();
  changeData(message: any) {
    this.messageSource.next(message);
  }
}
           

新的index元件如下:

@Component({
  selector: 'app-index',
  template: '<app-abc [shuru]="obs$"></app-abc>',
})
export class IndexComponent {
  shuru = 1;
  constructor(private obs$: ObsService) {
    setInterval(() => {
      this.obs$.changeData(this.shuru++);
    }, 1111);
  }
}
           

新的abc元件如下:

@Component({
  selector: 'app-abc',
  template: '  輸入是:{{shuru["comeOneData"]| async | json}}',
  changeDetection: ChangeDetectionStrategy.OnPush
})

export class AbcComponent implements OnChanges {
  @Input()
  shuru: Observable<any>;
  
  ngOnChanges(changes: SimpleChanges): void {
    for (let propName in changes) {
      let chng = changes[propName];
      let cur = JSON.stringify(chng.currentValue);
      let prev = JSON.stringify(chng.previousValue);
      console.log(`${propName}: currentValue = ${cur}, previousValue = ${prev}`);
    }
  }
}
           
angular中元件changeDetection為ChangeDetectionStrategy.OnPush時的學習

7、當輸入屬性是Observable的時候

這個其實和6有點類似的。

index元件是這樣的:

@Component({
  selector: 'app-index',
  template: '<app-abc [shuru]="shuru"></app-abc>',
})
export class IndexComponent {
  observer;
  num = 0;
  shuru = Observable.create((observer) => {
    this.observer = observer;
  });

  constructor() {
    setInterval(() => {
      this.observer.next(++this.num);
    }, 1111);
  }
}
           
7.1 訂閱方式

這個方法得多寫幾行代碼。。。在abc元件中subscribe輸入的Observable,取出來值之後自己做變更檢測插入到模闆中:

abc元件如下:

@Component({
  selector: 'app-abc',
  template: '  輸入是:{{num}}',
  changeDetection: ChangeDetectionStrategy.OnPush
})


export class AbcComponent implements OnChanges, OnInit {
  @Input()
  shuru: Observable<any>;
  num;

  constructor(private changeDetectorRef: ChangeDetectorRef,
  ) {
  }

  ngOnInit() {
    this.shuru.subscribe((res) => {
      console.log('subscribe',res);
      this.num = res;
      this.changeDetectorRef.detectChanges();
    });
  }

  ngOnChanges(changes: SimpleChanges): void {
    for (let propName in changes) {
      let chng = changes[propName];
      let cur = JSON.stringify(chng.currentValue);
      let prev = JSON.stringify(chng.previousValue);
      console.log(`${propName}: currentValue = ${cur}, previousValue = ${prev}`);
    }
  }
}
           
angular中元件changeDetection為ChangeDetectionStrategy.OnPush時的學習
7.2 管道

abc元件如下,少些好多代碼。不用手動做變更檢測了

@Component({
  selector: 'app-abc',
  template: '  輸入是:{{shuru|async}}',
  changeDetection: ChangeDetectionStrategy.OnPush
})


export class AbcComponent implements OnChanges {
  @Input()
  shuru: Observable<any>;

  constructor() {}

  ngOnChanges(changes: SimpleChanges): void {
    for (let propName in changes) {
      let chng = changes[propName];
      let cur = JSON.stringify(chng.currentValue);
      let prev = JSON.stringify(chng.previousValue);
      console.log(`${propName}: currentValue = ${cur}, previousValue = ${prev}`);
    }
  }
}
           

參考文獻

https://angular.io/api/core/ChangeDetectorRef

https://vsavkin.com/change-detection-in-angular-2-4f216b855d4c

https://blog.angular-university.io/onpush-change-detection-how-it-works/

https://blog.angularindepth.com/if-you-think-ngdocheck-means-your-component-is-being-checked-read-this-article-36ce63a3f3e5

https://www.stackchief.com/blog/ngDoCheck Example | Angular