Angular 角度2变化检测和变化检测策略.OnPush
我试图理解Angular 角度2变化检测和变化检测策略.OnPush,angular,zonejs,Angular,Zonejs,我试图理解ChangeDetectionStrategy.OnPush机制 我从读数中得出的结论是,变化检测通过比较旧值和新值来工作。如果对象引用没有更改,则该比较将返回false 然而,似乎在某些情况下,该“规则”被绕过。你能解释一下它是怎么工作的吗 *ngFor自己进行更改检测。每次运行更改检测时,NgFor将调用其ngDoCheck()方法,并在那里检查数组的内容是否已更改 在您的情况下,没有任何更改,因为构造函数是在Angular开始渲染视图之前执行的。 例如,如果您想添加一个按钮,如
ChangeDetectionStrategy.OnPush
机制
我从读数中得出的结论是,变化检测通过比较旧值和新值来工作。如果对象引用没有更改,则该比较将返回false
然而,似乎在某些情况下,该“规则”被绕过。你能解释一下它是怎么工作的吗
*ngFor
自己进行更改检测。每次运行更改检测时,NgFor
将调用其ngDoCheck()
方法,并在那里检查数组的内容是否已更改
在您的情况下,没有任何更改,因为构造函数是在Angular开始渲染视图之前执行的。例如,如果您想添加一个按钮,如
添加
然后单击实际上会导致ngFor
必须识别的更改
使用ChangeDetectionStrategy.OnPush将运行组件中的更改检测,因为当
- 收到绑定事件
(单击)
- 更改检测更新了
@Input()
| async
管道收到一个事件
- “手动”调用了更改检测
防止
应用。勾选尝试分离changeDetector:
constructor(private cd: ChangeDetectorRef) {
ngAfterViewInit() {
this.cd.detach();
}
好吧,因为我花了整整一个晚上才明白,所以我写了一份简历来解决脑子里的所有问题,这可能会对未来的读者有所帮助。所以,让我们先把一些事情弄清楚:
变化来自于事件
组件可能有字段。这些字段只有在某种事件之后才会更改,并且仅在该事件之后才会更改
我们可以将事件定义为鼠标单击、ajax请求、设置超时
数据从上到下流动
角度数据流是单向的。这意味着数据不会从孩子流向父母。仅从父级到子级,例如通过@Input
标记。使上层组件了解子组件中某些更改的唯一方法是通过事件。这让我们想到:
事件触发器更改检测
当一个事件发生时,角度框架从上到下检查每个组件,看看它们是否发生了变化如果有任何更改,它会相应地更新视图。
Angular在触发事件后检查每个组件。假设您在最低级别的组件上有一个单击事件,这意味着它有父级,但没有子级。这种单击可以通过事件发射器、服务等触发父组件中的更改。。安格尔不知道父母是否会改变。这就是Angular在默认情况下触发事件后检查每个组件的原因
要查看它们是否改变了角度,请使用ChangeDetector
类
变化检测器
每个组件都附带了一个变更检测器类。它用于检查组件在某些事件后是否已更改状态,并查看是否应更新视图。当事件发生时(鼠标单击等),默认情况下,所有组件都会发生此更改检测过程
例如,如果我们有一个ParentComponent:
@Component({
selector: 'comp-parent',
template:'<comp-child [name]="name"></comp-child>'
})
class ParentComponent{
name:string;
}
更改对象属性
您可能已经注意到,如果更改对象属性,isChanged方法将返回false。确实如此
let prop = {name:"cat"};
let oldProp = prop;
//change prop
prop.name = "dog";
oldProp === prop; //true
由于当对象属性可以更改而不在changeDetector
isChanged()
中返回true时,angular将假定下面的每个组件也可能已更改。因此,它将简单地检查所有组件中的更改检测
示例:这里我们有一个带有子组件的组件。虽然父组件的更改检测将返回false,但子组件的视图应该很好地更新
@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;
}
单击后,名称仍在视图中,但不在组件本身中
组件内部触发的事件将触发更改检测。
现在我们来谈谈我最初的问题中让我困惑的地方。下面的组件标有OnPush策略,但视图在更改时会更新
@组件({
选择器:“我的应用程序”,
模板:`
`,
风格:[`
.orange{背景:橙色;宽度:250px;高度:250px;}
`]
})
导出类应用程序{
person:person={name:“thierry”};
单击(){
this.person.name=“Jean”;
console.log(此.sub.person);
}
}
//子组件
@组成部分({
选择器:“子组件”,
changeDetection:ChangeDetectionStrategy.OnPush,
模板:`
{{person.name}
`,
风格:[`
.grey{背景:#ccc;宽度:100px;高度:100px;}
`]
})
导出类子组件{
@输入()
person:person={name:“jhon”};
单击(){
this.person.name=“密歇根”;
}
}
这里我们看到对象输入没有改变引用,我们使用的是策略OnPush。这可能会让我们相信它不会被更新。事实上,它是更新的
正如Gunter在回答中所说,这是因为,使用OnPush策略,在以下情况下,组件会发生变化检测:
- 在组件本身上接收(单击)绑定事件
- 更新了@Input()(如ref obj中的更改)
- |异步管道收到一个事件
- “手动”调用了更改检测
不管战略如何
链接
在angular中,我们高度使用父子结构。在那里,我们使用@输入将数据从父级传递给子级
在那里,如果在子级的任何祖先上发生更改,那么更改检测将在该祖先的组件树中发生
但是在大多数情况下,我们只需要在子对象的输入发生变化时更新子对象的视图(调用变化检测)。为了实现这一点,我们可以使用OnPush@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;
}
@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,
}
@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";
}
}
- The Input reference changes(Immutable inputs)
- An event originated from the component or one of its children
- Run change detection explicitly
- Use the async pipe in the view