问题描述
我正试图了解ChangeDetectionStrategy.OnPush
机制.
我从阅读中得出的结论是,更改检测通过将旧值与新值进行比较而起作用.如果对象引用未更改,则该比较将返回false.
What I gather from my readings is that a change detection works by comparing the old value to the new value. That comparison will returns false if the object reference hasn't changed.
但是,似乎在某些情况下会绕过该规则".您能解释一下这一切如何运作吗?
However there seems to be certain scenarios where that "rule" is bypassed. Could you explain how does it all work ?
推荐答案
好吧,因为这花了我整整一个晚上才明白我写了一份简历以解决所有问题,这可能对将来的读者有所帮助.因此,让我们首先清除一些问题:
Okay, since this took me a whole evening to understand I made a resume to settle everything in my head and it might help future readers. So let's start by clearing some things up:
一个组件可能具有字段.这些字段仅在发生某种事件之后才更改.
A component might have fields. Those fields only change after some sort of event, and only after that.
我们可以将事件定义为鼠标单击,ajax请求,setTimeout ...
We can define an event as a mouse click, ajax request, setTimeout...
角度数据流是一条单向街.这意味着数据不会从孩子流向父母.例如,仅通过@Input
标签从父级到子级.使上层组件知道孩子的某些变化的唯一方法是通过事件.这将我们带到:
Angular data flow is a one way street. That means that data doesn't flow from children to parents. Only from parent to children for instance via the @Input
tag. The only way to make a upper component aware of some change in a child is through an event. Which brings us to:
事件发生时,角度框架会从上到下检查每个组件,以查看它们是否已更改. 如果有任何更改,它将相应地更新视图.
When an event happens the angular framework check every component from top to bottom to see if they have changed. If any has changed, it updates the view accordingly.
触发事件后,Angular会检查每个组件.假设您在最低级别的组件上有一个单击事件,这意味着它有父级但没有子级.该单击可能会通过事件发射器,服务等触发父组件的更改.Angular不知道父组件是否会更改.这就是为什么在默认情况下触发事件后Angular会检查每个组件的原因.
Angular checks every components after an event has been fired. Say you have a click event on a component that is the component at the lowest level, meaning it has parents but no children. That click could trigger a change in a parent component via an event emitter, a service, etc.. Angular doesn't know if the parents will change or not. That is why Angular checks every components after an event has been fired by default.
要查看它们是否已更改角度,请使用ChangeDetector
类.
To see if they have changed angular use the ChangeDetector
class.
每个组件都附加有一个变更检测器类.它用于检查某个事件后组件是否已更改状态,并查看是否应更新视图.当发生事件(鼠标单击等)时,默认情况下,所有组件都会进行此更改检测过程.
Every component has a change detector class attached to it. It is used to check if a component has changed state after some event and to see if the view should be updated. When an event happen (mouse click, etc) this change detection process happens for all the components -by default-.
例如,如果我们有一个ParentComponent:
For example if we have a ParentComponent:
@Component({
selector: 'comp-parent',
template:'<comp-child [name]="name"></comp-child>'
})
class ParentComponent{
name:string;
}
我们将在ParentComponent
上附加一个变更检测器,如下所示:
We will have a change detector attached to the ParentComponent
that looks like this:
class ParentComponentChangeDetector{
oldName:string; // saves the old state of the component.
isChanged(newName){
if(this.oldName !== newName)
return true;
else
return false;
}
}
更改对象属性
您可能已经注意到,如果更改对象属性,则isChanged方法将返回false.确实
Changing object properties
As you might have notice the isChanged method will return false if you change an object property. Indeed
let prop = {name:"cat"};
let oldProp = prop;
//change prop
prop.name = "dog";
oldProp === prop; //true
由于可以更改对象属性而又不会在changeDetector
isChanged()
中返回true,因此angular会假定下面的每个组件也可能都已更改.因此,它将仅检查所有组件中的更改检测.
Since when an object property can change without returning true in the changeDetector
isChanged()
, angular will assume that every below component might have changed as well. So it will simply check for change detection in all components.
示例:这里我们有一个带有子组件的组件.虽然更改检测将为父组件返回false,但应该很好地更新子视图.
Example: here we have a component with a sub component. While the change detection will return false for the parent component, the view of the child should very well be updated.
@Component({
selector: 'parent-comp',
template: `
<div class="orange" (click)="person.name='frank'">
<sub-comp [person]="person"></sub-comp>
</div>
`
})
export class ParentComponent {
person:Person = { name: "thierry" };
}
// sub component
@Component({
selector: 'sub-comp',
template: `
<div>
{{person.name}}
</div>
})
export class SubComponent{
@Input("person")
person:Person;
}
这就是为什么默认行为是检查所有组件的原因.因为即使子组件的输入未更改也无法更改,但是angular仍无法确定其输入是否确实未更改.传递给它的对象可能相同,但是可能具有不同的属性.
That is why the default behavior is to check all components. Because even though a sub component cannot change if its input haven't changed, angular doesn't know for sure it's input haven't really changed. The object passed to it might be the same but it could have different properties.
用changeDetection: ChangeDetectionStrategy.OnPush
标记组件时,如果对象引用不变,则angular将假定输入对象不变.这意味着更改属性不会触发更改检测.因此,视图将与模型不同步.
When a component is marked with changeDetection: ChangeDetectionStrategy.OnPush
, angular will assume that the input object did not change if the object reference did not change. Meaning that changing a property won't trigger change detection. Thus the view will be out of sync with the model.
示例
这个例子很酷,因为它展示了这个例子.您有一个父组件,单击该组件时,输入对象名称属性会更改.如果在父组件中检查click()
方法,您会注意到它在控制台中输出子组件属性.该属性已更改..但是您无法从视觉上看到它.这是因为视图尚未更新.由于采用了OnPush策略,因此更改检测过程没有发生,因为ref对象没有更改.
This example is cool because it shows this in action. You have a parent component that when clicked the input object name properties is changed.If you check the click()
method inside the parent component you will notice it outputs the child component property in the console. That property has changed..But you can't see it visually. That's because the view hasn't been updated. Because of the OnPush strategy the change detection process didn't happen because the ref object didn't change.
@Component({
selector: 'my-app',
template: `
<div class="orange" (click)="click()">
<sub-comp [person]="person" #sub></sub-comp>
</div>
`
})
export class App {
person:Person = { name: "thierry" };
@ViewChild("sub") sub;
click(){
this.person.name = "Jean";
console.log(this.sub.person);
}
}
// sub component
@Component({
selector: 'sub-comp',
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<div>
{{person.name}}
</div>
`
})
export class SubComponent{
@Input("person")
person:Person;
}
export interface Person{
name:string,
}
单击后,名称在视图中仍然是thierry,但在组件本身中不是
在这里,我们来解决最初的问题让我感到困惑.下面的组件标记有OnPush策略,但是视图在更改时会更新.
Here we come to what confused me in my original question. The component below is marked with the OnPush strategy, yet the view is updated when it changes..
@Component({
selector: 'my-app',
template: `
<div class="orange" >
<sub-comp ></sub-comp>
</div>
`,
styles:[`
.orange{ background:orange; width:250px; height:250px;}
`]
})
export class App {
person:Person = { name: "thierry" };
click(){
this.person.name = "Jean";
console.log(this.sub.person);
}
}
// sub component
@Component({
selector: 'sub-comp',
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<div class="grey" (click)="click()">
{{person.name}}
</div>
`,
styles:[`
.grey{ background:#ccc; width:100px; height:100px;}
`]
})
export class SubComponent{
@Input()
person:Person = { name:"jhon" };
click(){
this.person.name = "mich";
}
}
因此,在这里我们看到对象输入没有更改引用,并且我们使用的是OnPush策略.这可能使我们相信它不会被更新.实际上,它已更新.
So here we see that the object input hasn't changed reference and we are using strategy OnPush. Which might lead us to believe that it won't be updated. In fact it is updated.
正如Gunter在回答中所说,这是因为,采用OnPush策略时,如果发生以下情况,就会对组件进行更改检测:
As Gunter said in his answer, that is because, with the OnPush strategy the change detection happens for a component if:
- 在组件本身上收到绑定事件(单击).
- @Input()已更新(如ref obj更改)
- |异步管道收到事件
- 更改检测是手动"调用的
不管采取什么策略.
- https://hackernoon. com/您需要了解的有关角度变化的检测信息,网址为8006c51d206f
- http://blog.angular -university.io/how-does-angular-2-change-detection-really-work/
- https://angular-2-training- book.rangle.io/handout/change-detection/change_detector_classes.html
- https://www.youtube.com/watch?v=X0DLP_rktsc
- https://hackernoon.com/everything-you-need-to-know-about-change-detection-in-angular-8006c51d206f
- http://blog.angular-university.io/how-does-angular-2-change-detection-really-work/
- https://angular-2-training-book.rangle.io/handout/change-detection/change_detector_classes.html
- https://www.youtube.com/watch?v=X0DLP_rktsc
这篇关于角度2变化检测和ChangeDetectionStrategy.OnPush的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!