Angular 状态改变时重新渲染组件列表

Angular re-renders list of components when state is changed without mutation

我来自 React,我已经使用 Angular 几个月了。 React 组件在重新渲染时仅更新必要的 HTML,而 Angular 组件似乎在状态未发生变化时完全重新渲染。如果我直接改变状态似乎工作得很好。

这是我最基本的例子:

我有主要组件,它保存状态:

items = [{ name: "John", age: 8 }, { name: "Jane", age: 20 }];

并在其视图中呈现项目列表:

<item *ngFor="let item of items" [item]="item"></item>

item 组件如下所示:

@Component({
  selector: "item",
  template: `
    <p>
      {{ item.name }}, {{ item.age }}
      <input type="checkbox" />
    </p>
  `
})
export class ItemComponent {
  @Input() item: any;
}

我在项目组件中添加了那个复选框,只是为了注意到组件何时完全重新呈现。

所以我要做的是勾选复选框并为该州的每个用户增加 age

如果我通过改变状态来增加它,视图会按预期更新并且复选框保持选中状态:

this.items.forEach(item => {
  item.age++;
});

但是如果我更改 age 而不直接改变状态,整个列表将重新呈现并且复选框不再被选中:

this.items = this.items.map(item => ({
  ...item,
  age: item.age + 1
}));

这是 CodeSandbox 中的完整示例。

任何人都可以解释为什么会发生这种情况以及当我不改变状态时如何使列表不完全重新呈现?

NgForOf Angular 用于呈现项目列表的指令检查我们传递给它的数组是否有任何更改。

如果您在项目中改变 属性,那么 Angular 知道没有变化,因为对数组中对象的引用保持不变。

另一方面,如果您用新对象完全替换以前的对象,则 ngForOf 会发出重新渲染该对象的信号。

要确定对象是否更改 ngForOf 指令,请使用 DefaultIterableDiffer,如果我们提供了它,它会查看 trackByFn 函数。如果我们不提供它,那么默认函数如下所示:

var trackByIdentity = function (index, item) { 
  return item;
};

然后 Angular compares 此函数的结果与集合中的先前值。

你可以像 React 中的 key 一样思考 trackByFn

因此您的解决方案可能如下所示:

*ngFor="let item of items; trackBy: trackByFn"

trackByFn(i) {
    return i;
}

https://codesandbox.io/s/angular-67vo6