为什么即使设置了 OnPush 标志,Angular2 在更改检测期间也会捕获引用更改和原始更改?

Why is both reference change and primitive change catch by Angular2 during change detection even with OnPush flag set?

考虑以下 code

import {Component, OnInit, Input, OnChanges, DoCheck, ChangeDetectionStrategy} from 'angular2/core'

@Component({
  selector: 'child1',
  template: `
    <div>reference change for entire object: {{my_obj1.name}}</div>
    <div>reassign primitive in property of object: {{my_obj2.name}}</div>
    <div>update primitive in property of object: {{my_obj2.num}}</div>
    `,
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class Child1 {

  @Input()
  my_obj1: Object = {'name': ''};

  @Input()
  my_obj2: Object = {'name': '', 'num': 0};

  ngDoCheck() {
    console.log('check from child1');
    console.log(this.my_obj1);
    console.log(this.my_obj2);
  }
}

@Component({
  selector: 'parent',
  template: `
    <div>
      <child1
        [my_obj1]="my_obj1"
        [my_obj2]="my_obj2"
      >
      </child1>
      <button (click)="change_obj1()">
        Change obj1
      </button>

    </div>
    `,
  directives: [Child1]
})
export class App {

  my_obj1: Object = {'name': 'name1'};
  my_obj2: Object = {'name': 'name2', 'num': 0};

  change_obj1() {
    this.my_obj1 = {'name': 'change1'}
    this.my_obj2['name'] = 'change2';
    this.my_obj2['num'] += 1;
  }
}

根据我所做的实验,这是我对当前Angular2变化检测策略的理解,是否有人可以验证它是否正确?

  1. Angular2 在进行变化检测时默认检查值是否相等。如果没有 ChangeDetectionStrategy.OnPush,则检查组件树中的每个监视变量的值是否相等。如果值相等为假,则该特定组件将重新渲染,如果值相等为真,则该特定组件将不会重新渲染。

  2. 如果给一个组件添加ChangeDetectionStrategy.OnPush。行为变化如下

    我。如果组件内部的变量有引用变化,则重新渲染组件,并检查子组件的变化检测(其具体的变化检测算法value/reference检查取决于ChangeDetectionStrategy.OnPush

    二。如果组件内的变量没有引用更改,则不会重新渲染组件,也不会检查子组件的更改检测,无论是否存在 ChangeDetectionStrategy.OnPush

这是正确的解释吗?

这篇post解释的很详细:

http://victorsavkin.com/post/133936129316/angular-immutability-and-encapsulation

简而言之,您的假设是正确的。 Angular2 必须保守并检查值是否相等,即它必须对引用的对象执行 'deep check'。

ChangeDetectionStrategy.OnPush 中,仅当对其输入对象的引用发生更改时,组件才会更新。

这就是为什么不可变对象可以成为首选数据结构的原因——如果我们必须更新一个对象,组件现在正在引用一个新对象。因此 angular 很容易知道必须更新哪些组件。

性能行为也可以通过 ChangeDetectorRef.markForCheck(); 方法通过可观察对象实现。

这里有解释:

http://blog.thoughtram.io/angular/2016/02/22/angular-2-change-detection-explained.html

我稍微修改了你的 plunker:new plunker

由于原始值是 immutable,重新分配和更新之间没有区别——原始值获得新的不可变值,所以我删除了 "updating" 代码。此外,将分配新的对象引用(触发变更检测)和分配新的原始值(不触发变更检测)分开是非常有帮助的。所以我也这样做了。

如果你 运行 我 Plunker 我们可以做出以下观察:

  1. 更改 OnPush 组件上的 引用类型 的输入 属性 将更新 组件的视图。检查模板绑定是否有更改。此外,还会检查子组件(假设它们未使用 OnPush)。
  2. 更改 OnPush 组件的引用类型中包含的基元 属性 不会更新 组件的视图。不检查模板绑定是否有更改。此外,不检查子组件,无论它们是否使用 OnPush
  3. ngDoCheck() 总是在第一个 OnPush 组件上调用, 不管 是否检查模板绑定是否发生变化。我觉得这很奇怪(谁知道,也许这是一个错误)。因此,调用 ngDoCheck() 并不一定意味着检查了模板绑定。

请注意,当检测到模板绑定更改时,只有该更改会传播到子组件或 DOM(视情况而定,具体取决于绑定类型)。如果绑定更改导致 DOM 更改,则不会重新呈现整个组件。只有绑定的 DOM 数据会更新,浏览器只会更新那个 DOM 元素。 (这与其他一些框架不同,如果发现任何更改,它们会重新呈现整个模板。这有助于使 Angular 更快。)