为什么即使设置了 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
变化检测策略的理解,是否有人可以验证它是否正确?
Angular2 在进行变化检测时默认检查值是否相等。如果没有 ChangeDetectionStrategy.OnPush
,则检查组件树中的每个监视变量的值是否相等。如果值相等为假,则该特定组件将重新渲染,如果值相等为真,则该特定组件将不会重新渲染。
如果给一个组件添加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 我们可以做出以下观察:
- 更改
OnPush
组件上的 引用类型 的输入 属性
将更新 组件的视图。检查模板绑定是否有更改。此外,还会检查子组件(假设它们未使用 OnPush
)。
- 更改
OnPush
组件的引用类型中包含的基元 属性 不会更新 组件的视图。不检查模板绑定是否有更改。此外,不检查子组件,无论它们是否使用 OnPush
。
ngDoCheck()
总是在第一个 OnPush
组件上调用, 不管 是否检查模板绑定是否发生变化。我觉得这很奇怪(谁知道,也许这是一个错误)。因此,调用 ngDoCheck()
并不一定意味着检查了模板绑定。
请注意,当检测到模板绑定更改时,只有该更改会传播到子组件或 DOM(视情况而定,具体取决于绑定类型)。如果绑定更改导致 DOM 更改,则不会重新呈现整个组件。只有绑定的 DOM 数据会更新,浏览器只会更新那个 DOM 元素。 (这与其他一些框架不同,如果发现任何更改,它们会重新呈现整个模板。这有助于使 Angular 更快。)
考虑以下 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
变化检测策略的理解,是否有人可以验证它是否正确?
Angular2 在进行变化检测时默认检查值是否相等。如果没有
ChangeDetectionStrategy.OnPush
,则检查组件树中的每个监视变量的值是否相等。如果值相等为假,则该特定组件将重新渲染,如果值相等为真,则该特定组件将不会重新渲染。如果给一个组件添加
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 我们可以做出以下观察:
- 更改
OnPush
组件上的 引用类型 的输入 属性 将更新 组件的视图。检查模板绑定是否有更改。此外,还会检查子组件(假设它们未使用OnPush
)。 - 更改
OnPush
组件的引用类型中包含的基元 属性 不会更新 组件的视图。不检查模板绑定是否有更改。此外,不检查子组件,无论它们是否使用OnPush
。 ngDoCheck()
总是在第一个OnPush
组件上调用, 不管 是否检查模板绑定是否发生变化。我觉得这很奇怪(谁知道,也许这是一个错误)。因此,调用ngDoCheck()
并不一定意味着检查了模板绑定。
请注意,当检测到模板绑定更改时,只有该更改会传播到子组件或 DOM(视情况而定,具体取决于绑定类型)。如果绑定更改导致 DOM 更改,则不会重新呈现整个组件。只有绑定的 DOM 数据会更新,浏览器只会更新那个 DOM 元素。 (这与其他一些框架不同,如果发现任何更改,它们会重新呈现整个模板。这有助于使 Angular 更快。)