详解Angular中的变更检测(十二)- OnPush策略示例二

71 阅读1分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第10天,点击查看活动详情

示例四

markForCheckdetectChanges的区别是什么呢?

  • detectChanges, 它会触发当前组件和子组件的变化检测;
  • markForCheck, 它不会触发变更检测,但是会把当前的OnPush组件和所有的父组件为OnPush的组件 标记为需要检测状态,在当前或者下一个变化检测周期进行检测;
@Component({
  selector: 'app',
  styleUrls: ['./app.component.scss'],
  template: `
      <h1>{{count}}</h1>
      <button (click)="normalClick()">Click</button>
      <main [options]="options" [actionSubject$]="actionSubject$"></main>`
})
export class AppComponent  {
  options: any = { name: 'Tom' };
  count: number = 0;

   normalClick(): void {
    this.count++;
    this.actionSubject$.next(this.count);
  }
}
@Component({
  selector: 'main',
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `
    <h1>{{count}}</h1>
  `
})
export class MainComponent {
  @Input() options: any;
  @Input() actionSubject$!: Subject<any>;
  count: number = 0;

  constructor(private ref: ChangeDetectorRef){}
  
   ngOnInit(){
    this.actionSubject$.subscribe((d: any) => {
      this.count = d;

      if (this.count >= 3 && this.count <= 6) {
        this.ref.markForCheck();
        this.count += 100;
      }
    });
   }
}

上面的代码,当App组件中Button点击三次后,Main组件中显示的是多少呢?

显示的是103,因为markForCheck() 会把当前组件和父组件都标记为需要check,也就是当到变更检测时,会执行变更检测。标记完后,变更检测并没有发生,紧接着count的值加100,那就是103。当异步任务执行完成后,进行变更检测时,发现OnPush被标记为需要检测,那执行完变更检测后,页面上会显示103。

...
export class MainComponent {
  ...
  
  constructor(private ref: ChangeDetectorRef){}
  
   ngOnInit(){
    this.actionSubject$.subscribe((d: any) => {
      this.count = d;

      if (this.count >= 3 && this.count <= 6) {
        this.ref.detectChanges();
        this.count += 100;
      }
    });
   }
}

上面代码中,当count值等于3后,detectChanges() 会理解执行一次变更检测,所以页面上的值显示为3,紧接着,count值加100,count的值等于103,。等到变更检测时,发现是OnPush,则跳过变更检测。

...
export class MainComponent {
  ...
  
  constructor(private ref: ChangeDetectorRef){}
  
   ngOnInit(){
    this.actionSubject$.subscribe((d: any) => {
      this.count = d;

      if (this.count >= 3 && this.count <= 6) {
        // this.ref.markForCheck();
        this.ref.detectChanges();
      }
    });
   }
}

上面代码并不会有任何区别。

需要注意的是,可能会在ngDoCheck中改变count的值。

...
export class MainComponent {
  ...
  
  constructor(private ref: ChangeDetectorRef){}
  
   ngOnInit(){
    this.actionSubject$.subscribe((d: any) => {
      this.count += d;

      if (this.count >= 3 && this.count <= 6) {
        // this.ref.markForCheck();
        this.ref.detectChanges();
      }
    });
   }

  ngDoCheck(){
      this.count++;
  }
}

这个自己思考一下具体的结果吧。

一个是4,一个是3。

示例五

@Component({
  selector: 'main',
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `
  <h1>{{count}}</h1>
  `
})
export class MainComponent {
  count: number = 0;
  
  constructor(private ref: ChangeDetectorRef){}
  
   ngOnInit(){
    ...
   }

  ngAfterViewInit(): void {
    this.count++;
  }
}

上面代码,如果是OnPush策略的话,页面加载完成后,显示多少?如果是Default策略的话,显示多少呢?这些差异,其实都是和变更检测策略有关系的。

OnPush下,并不会报错,而且页面上显示的0。

@Component({
  selector: 'main',
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `
  <h1>{{count}}</h1>
  `
})
export class MainComponent {
  count: number = 0;
  
  constructor(private ref: ChangeDetectorRef){}
  
   ngOnInit(){
    ...
   }

  ngAfterViewInit(): void {
    this.count++;
    this.ref.checkNoChanges();
  }
}

需要注意的是,tick()方法里的view.checkNoChanges() 并没有起作用,所以没有报错。但是,上面代码中使用了组件变更检测器中的checkNoChanges(), 它是起作用的。所以,控制台会报错。所以,方法名一样,但是功能是有区别的。

看了这些例子后,是不是对OnPush策略理解更加深刻了呢。