代码之家  ›  专栏  ›  技术社区  ›  Felix

Angular:为什么我需要调用cdr.markForCheck()来实现OnPush策略?为什么detectChanges()不够用?

  •  0
  • Felix  · 技术社区  · 6 年前

    我仍然不明白两者之间有什么区别 cdr.detectChanges() 和 cdr.markForCheck() 对于 OnPush 从使用角度改变检测策略。

    尽管我读过 this 所以问题和 InDepth 解释。

    为什么我不能 call cdr.detectChanges() ? 为什么我需要标记从当前组件到根的树 (或者是由于推迟到下一个检测周期)?

    还是也需要更新父组件?

    在以下示例中,这两种方法都有效:

    import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core';
    
    @Component({
      selector: 'app-test',
      template: `{{ i }}`,
      changeDetection: ChangeDetectionStrategy.OnPush
    })
    export class TestComponent implements OnInit {
      private i = 0;
    
      constructor(private cdr: ChangeDetectorRef) { }
    
      ngOnInit() {
        setInterval(() => {
          this.i++;
          // this.cdr.detectChanges(); // this works too and updates view
          this.cdr.markForCheck();     // but this is for some reason recommended
        }, 1000);
      }
    }
    
    0 回复  |  直到 6 年前
        1
  •  3
  •   JohnnyDevNull    6 年前

    detectChanges 直接为组件运行更改检测循环
    markForCheck 它只是标记组件以供检查

    在您的示例中,您运行 setInterval ,这意味着changeDetection由setInterval触发,但在下一个循环中在您的组件中运行。

    这是因为 NgZone 做一些类似monkey的事情来修补它可以运行的所有默认api changeDetection .使用on Push,您可以挂在那里,告诉您的组件,当它必须运行额外的检查时,您会告诉angular。

    如果你想深入了解NgZone和ChangeDetection,请观看此视频 Angular Performance: Your App at the Speed of Light 这包含了关于ChangeDetection和NgZone工作原理的非常好的可理解的知识,每个Angular开发人员都应该知道。

        2
  •  1
  •   Ray J    5 年前

    对此不是100%确定,但据我所知,使用 detectChanges() 任何地方的性能都可能不如使用 markForCheck() .

    使用 检测更改()

    想象 TestComponent 有几个字段 foo , bar , baz ,每个字段的设置器如下:

    set foo(value) {
      ...
      this.cdr.detectChanges();
    };
    

    一些异步事件可以做到:

    testComponent.foo = ...; // triggers 1st change detection cycle on TestComponent and its children
    testComponent.bar = ...; // triggers 2nd change detection cycle on TestComponent and its children
    testComponent.baz = ...; // triggers 3rd change detection cycle on TestComponent and its children
    
    // after the async event, Angular runs regular change detection
    // starting from the root component,
    // TestComponent was not marked for check so it is skipped.
    

    总的来说,我们已经对 测试组件 3次,但这是不必要的工作,我们本可以在所有三次更改后完成一个更改检测周期。

    注意:您可以重写代码并限制 测试组件 的公共API进行优化 检测更改() 使用,但这完全取决于你手动解决。

    使用 markForCheck()

    现在让我们假设setter使用 markForCheck() :

    set foo(value) {
      ...
      this.cdr.markForCheck();
    };
    

    一些异步事件可以做到:

    testComponent.foo = ...; // no change detection triggered
    testComponent.bar = ...; // no change detection triggered
    testComponent.baz = ...; // no change detection triggered
    
    // after the async event, Angular runs regular change detection
    // starting from the root component,
    // TestComponent was marked for check so change detection runs on it.
    

    总的来说,更改检测只运行一次 测试组件 。在上运行更改检测的成本越高 测试组件 (例如,如果 测试组件 具有庞大的子组件子树),这就越重要。