Angular 更改检测过程重绘 dom
Angular change detection process repainting the dom
我正在了解 Angular 变更检测过程,我正在检查 Chrome 开发工具,我看到了奇怪的行为。
我的 plnkr 演示行为:http://plnkr.co/edit/cTLF00nQdhVmkHYc8IOu
我有一个简单的组件视图:
<li *ngFor="let item of list">{{item.name}}</li>
及其构造函数:
constructor() {
this.list = [{name: 'Gustavo'}, {name: 'Costa'}]
为了模拟我添加的简单请求:
// simulating request repaint the DOM
setInterval( () => {
this.list = [{name: 'Gustavo'}, {name: 'Costa'}];
}, 2000);
如果您注意到,数组 list
收到一个等于初始值的列表。让我们想象一下,当 Angular 在变化检测过程中检查视图中的值时,我们有这样的代码:
if( oldName !== name ) { // ( 'Gustavo' !== 'Gustavo')
// update the view
}
但值是相同的,为什么 angular 每 2 秒重新绘制 DOM。?
但是如果我改变对象,重绘不会发生
// simulating request there is not repaint
setInterval( () => {
this.list[0].name = "Gustavo"; // no repaint because it's the same value
this.list[1].name = "Costa 2"; // repaint
}, 2000);
您可以使用上面的 plnkr link 进行测试。
这是因为您每次都在创建一个 new 数组并且 angular 正在更新,因为引用已更改。如果你每 2 秒将它分配给同一个引用,情况就不会如此
otherList = [{name: 'Gustavo'}, {name: 'Costa'}];
constructor() {
this.list = [{name: 'Gustavo'}, {name: 'Costa'}]
setInterval( () => {
this.list = this.otherList;
}, 2000);
}
添加到@0mpurdy,Objects
(Arrays
也是如此)永远不会相等,即使它们具有相同的属性和值,除非一个是另一个的引用或者它们都共享相同的引用。
另一方面,基元可以等于其他基元,因为它们是按值而不是按引用进行比较的。这就是为什么当您为相同的值手动覆盖值时不会触发变化检测,但是如果您替换整个事物,即使对象明显相等,也会触发变化检测。
这是因为 Angular 使用默认值 trackByFunction
作为 DefaultIterableDiffer
按身份跟踪项目。
const trackByIdentity = (index: number, item: any) => item;
很明显,当您创建一个新数组时,它会创建新的对象引用并且 Angular 会检测到更改。即使您没有更改数组引用,Angular 仍会认为项目已更改,因为对象引用已更改:
setInterval( () => {
this.list.length = 0;
this.list.push({name: 'Gustavo'});
this.list.push({name: 'Costa'});
}, 2000);
您可以为您提供自定义 trackByFunction
以按对象名称进行跟踪:
@Component({
selector: 'my-app',
template: `
<li *ngFor="let item of list; trackBy:identify">{{item.name}}</li>
`
})
export class App {
list:[];
identify(index, item){
return item.name;
}
这样DOM就不会更新了。参见 this plunker。
既然您对 ngFor
感到好奇,您也可以阅读 ,我在其中解释了 ngFor
的工作原理。
我正在了解 Angular 变更检测过程,我正在检查 Chrome 开发工具,我看到了奇怪的行为。
我的 plnkr 演示行为:http://plnkr.co/edit/cTLF00nQdhVmkHYc8IOu
我有一个简单的组件视图:
<li *ngFor="let item of list">{{item.name}}</li>
及其构造函数:
constructor() {
this.list = [{name: 'Gustavo'}, {name: 'Costa'}]
为了模拟我添加的简单请求:
// simulating request repaint the DOM
setInterval( () => {
this.list = [{name: 'Gustavo'}, {name: 'Costa'}];
}, 2000);
如果您注意到,数组 list
收到一个等于初始值的列表。让我们想象一下,当 Angular 在变化检测过程中检查视图中的值时,我们有这样的代码:
if( oldName !== name ) { // ( 'Gustavo' !== 'Gustavo')
// update the view
}
但值是相同的,为什么 angular 每 2 秒重新绘制 DOM。?
但是如果我改变对象,重绘不会发生
// simulating request there is not repaint
setInterval( () => {
this.list[0].name = "Gustavo"; // no repaint because it's the same value
this.list[1].name = "Costa 2"; // repaint
}, 2000);
您可以使用上面的 plnkr link 进行测试。
这是因为您每次都在创建一个 new 数组并且 angular 正在更新,因为引用已更改。如果你每 2 秒将它分配给同一个引用,情况就不会如此
otherList = [{name: 'Gustavo'}, {name: 'Costa'}];
constructor() {
this.list = [{name: 'Gustavo'}, {name: 'Costa'}]
setInterval( () => {
this.list = this.otherList;
}, 2000);
}
添加到@0mpurdy,Objects
(Arrays
也是如此)永远不会相等,即使它们具有相同的属性和值,除非一个是另一个的引用或者它们都共享相同的引用。
另一方面,基元可以等于其他基元,因为它们是按值而不是按引用进行比较的。这就是为什么当您为相同的值手动覆盖值时不会触发变化检测,但是如果您替换整个事物,即使对象明显相等,也会触发变化检测。
这是因为 Angular 使用默认值 trackByFunction
作为 DefaultIterableDiffer
按身份跟踪项目。
const trackByIdentity = (index: number, item: any) => item;
很明显,当您创建一个新数组时,它会创建新的对象引用并且 Angular 会检测到更改。即使您没有更改数组引用,Angular 仍会认为项目已更改,因为对象引用已更改:
setInterval( () => {
this.list.length = 0;
this.list.push({name: 'Gustavo'});
this.list.push({name: 'Costa'});
}, 2000);
您可以为您提供自定义 trackByFunction
以按对象名称进行跟踪:
@Component({
selector: 'my-app',
template: `
<li *ngFor="let item of list; trackBy:identify">{{item.name}}</li>
`
})
export class App {
list:[];
identify(index, item){
return item.name;
}
这样DOM就不会更新了。参见 this plunker。
既然您对 ngFor
感到好奇,您也可以阅读 ngFor
的工作原理。