ChangeDetectionStrategy.OnPush和re-redering / domchanges

时间:2017-07-18 14:08:10

标签: angular ngrx-store

我试图找出如何将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 }));

或者如果我在reducer中设置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,即使没有。

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

1 个答案:

答案 0 :(得分:1)

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

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

当Angular 由于OnPushOnPush而停止时,组件的视图及其子项未更新。输入绑定的不可变状态意味着视图没有改变。

如果您有可观察的更改markForCheck策略的组件的内部状态。您必须在ChangeDetectRef上致电OnPush。这标志着该组件及其所有组件的父级用于更改检测。

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

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

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

<强>已更新

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

  

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

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

ngForOf将每次重复数组中的所有项目进行更改检测。对于每个项目,它将按功能调用轨道,或者它将哈希值附加到项目。这是它告诉数组中的项是否已更改的方式。他们可以通过删除或重新订购来改变。

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

如果数组引用保持不变,或者将其更改为新数组,则

ngForOf执行相同的工作量。它仍然必须迭代每个项目。 <div *ngFor="let entity of collection| async;let i = index;trackBy:entity?.id" [class.selected]="entity.isSelected"> 无法解决的问题是哈希值消失的时间。它必须削减旧视图并为每个项目创建一个新的DOM视图。

让我们来看看你的例子:

trackBy

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

  • trackBy必须给出一个函数引用。这已从Angular 1更改为缩小代码的要求。
  • 当实体值不存在时,表达式undefined会产生ngForOfundefined无法跟踪ngForOf,并且会被迫为每个未定义项目削减并重建DOM。
  • 除非属性entity?.id是函数,否则
  • id无法跟踪ngForOf。我认为这是一个属性值,entity将忽略它。
  • 最后,我认为 public trackEntity(indx: number, value: any) { if('id' in value) { return value.id; } return value || indx; } 变量不存在于ngForOf表达式的范围内。所以trackBy不会工作。

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

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

undefined

以上内容将尝试按实体ID属性进行跟踪,但如果您有trackBy项,则会回退到索引偏移量。

在您的模板中,您需要将以上功能用于<div *ngFor="let entity of collection| async;let i = index;trackBy:trackEntity" [class.selected]="entity.isSelected">

()

请注意,没有/^\+36(20|30|31).../ 个大括号。该函数正在通过引用传递。