0

我试图找出我应该如何结合使用 ngrx-store 和 OnPush changedetection 策略。

假设我想在我的集合中设置一个选定实体的类。
如果我做这样的事情:

 this.collection = Observable.combineLatest(
      this.store.let(fromStore.getCollection),
      this.store.let(fromStore.getSelected),
      (c, s) => c.map(entity => {return { ...entity, isSelected : s.id === entity.id ? true: 
 false }));

或者如果我在减速器中设置 isSelected 属性,它会创建我所有实体的克隆。如果我这样做的话,会有大量的收藏:

<div *ngFor="let entity of collection| async;let i = index;trackBy:entity?.id"
        [class.selected]="entity.isSelected">

它很慢!

但是,如果我不订阅这样的选择更改:

this.collection = this.store.let(fromStore.getCollection);

改变

<div ... [class.selected]="entity.isSelected">

<div ...[class.selected]="isSelected(entity.id) | async">

并创建一个获取选定对象的函数:

  public isSelected(id): Observable<boolean> {
    return this.selected && this.selected.find(s => !!s.id === id));
  }

它很快

因此,如果流发生更改,使用流的组件似乎需要大量时间来检测 domchanges,即使没有。

那是对的吗?这意味着您必须非常清楚您在商店中所做的更改以及您应该在组件中执行的操作。

4

1 回答 1

1

OnPush策略与组件的模板表达式无关。它告诉 Angular 组件的所有@Input()绑定都是不可变的,并且组件的子组件是依赖的。

当 Angular 进行更改检测并到达一个组件时,OnPush它将当前输入值与之前的输入值进行比较。如果所有这些输入仍然相等,则检测将在该组件处停止。它的孩子都不会被检查。

当 Angular因组件视图而停止OnPush时,它的子组件不会更新。输入绑定的不可变状态意味着视图没有改变。

如果您有可更改策略组件内部状态的可观察对象OnPush。你必须打电话markForCheckChangeDetectRef这标志着该组件及其所有父组件的更改检测。

当您没有OnPush正确使用时,您所描述的延迟只是意外行为的一部分。

这是一篇关于 Angular 中的变更检测的好文章:

https://blog.thoughtram.io/angular/2016/02/22/angular-2-change-detection-explained.html

更新

OnPush策略只影响你的组件。它不会改变 ngForOf 指令或您在模板中使用的其他组件的行为。

假设我有 200 个项目,其中 2 个项目被更改。我做了一个 array.map 并改变了这两个项目......

这是正确的策略。而不是修改现有数组并替换/删除项目。最好生成一个新数组,以便 Angular 可以快速看到它已更改。

ngForOf每次必须进行更改检测时,都会迭代数组中的所有项目。对于每个项目,它将按函数调用跟踪,或者将哈希值附加到项目。这就是它如何判断数组中的项目是否已更改。它们可以通过被删除或重新排序来改变。

ngForOf为数组中的每个项目维护一个视图。它将迭代每个项目,然后对每个视图执行更改检测。

ngForOf如果数组引用保持不变或更改为新数组,则执行相同数量的工作。它仍然必须迭代每个项目。ngForOf无法解决的是哈希值何时消失。它必须去除旧视图并为每个项目创建一个新的 DOM 视图。

让我们看看你的例子:

<div *ngFor="let entity of collection| async;let i = index;trackBy:entity?.id" [class.selected]="entity.isSelected">

您正在使用trackBy带有此表达式的entity?.id。这种表达方式有几个问题。

  • 必须给 trackBy 一个函数引用。这是从 Angular 1 更改为缩小代码的要求。
  • 当实体值不存在时,表达式entity?产生。无法跟踪并且将被迫为每个未定义的项目去皮重并重建 DOM。undefinedngForOfundefined
  • ngForOfentity?.id除非属性id是函数,否则无法跟踪。我认为这是一个属性值,ngForOf将忽略它。
  • 最后,我认为该entity变量不存在于 ngForOf 表达式的范围内。所以 trackBy 将不起作用。

这意味着每次重新创建数组时,DOM 都会重新构建,而且速度会非常慢。

您需要在组件中使用有效的跟踪功能:

 public trackEntity(indx: number, value: any) {
     if('id' in value) {
           return value.id;
     }
     return value || indx;
 }

以上将尝试通过实体 ID 属性进行跟踪,但如果您有undefined项目,这将回退到索引偏移量。

在您的模板中,您需要使用上述功能trackBy

<div *ngFor="let entity of collection| async;let i = index;trackBy:trackEntity" [class.selected]="entity.isSelected">

请注意,没有()大括号。该函数是通过引用传递的。

于 2017-07-18T14:26:13.100 回答