Angular2 ng For OnPush变化检测与阵列突变

时间:2016-11-27 14:21:12

标签: angular angular2-changedetection

我有一个数据表组件(angular2-data-table)项目,我们将项目从Angular的传统更改检测更改为OnPush,以优化渲染速度。

一旦实施了新的变更检测策略,就会在数据对象发生变异时引用该表而未更新的错误,例如对象的属性更新参考:https://github.com/swimlane/angular2-data-table/issues/255。对于诸如内联编辑或外部数据更改等大型数据集合(如股票代码)中的单个属性,可以使用强大的用例。

为了解决这个问题,我们添加了一个名为trackByProp的自定义trackBy属性检查器。参考:commit。不幸的是,这个解决方案没有解决问题。

在实时重新加载的demo page下,您可以看到上面提交中引用的演示,但是在您点击这样触发更改检测之前不会更新表。

组件的结构类似于:

Table > Body > Row Group > Row > Cell

所有这些组件都实现了OnPush。我在行设置器中使用getter / setter来触发页面重新计算,如here所示。

我们希望对那些实现此模式的人保持OnPush更改检测,但是,作为一个包含多个消费者的开源项目,人们可能会争论某些可见行的自定义检查功能屏幕上的值。

所有这一切,trackBy没有触发行单元格值的变化检测,实现这一目标的最佳方法是什么?

4 个答案:

答案 0 :(得分:23)

Angular2变化检测不会检查数组或对象的内容。

一个hacky解决方法是在变异后创建一个数组副本

this.myArray.push(newItem);
this.myArray = this.myArray.slice();

这种方式this.myArray引用不同的数组实例,Angular将识别更改。

另一种方法是使用IterableDiffer(对于数组)或KeyValueDiffer(对象)

// inject a differ implementation 
constructor(differs: KeyValueDiffers) {
  // store the initial value to compare with
  this.differ = differs.find({}).create(null);
}

@Input() data: any;

ngDoCheck() {
  var changes = this.differ.diff(this.data); // check for changes
  if (changes && this.initialized) {
    // do something if changes were found
  }
}

另见https://github.com/angular/angular/blob/14ee75924b6ae770115f7f260d720efa8bfb576a/modules/%40angular/common/src/directives/ng_class.ts#L122

答案 1 :(得分:3)

您可能希望使用markForCheck中的ChangeDetectorRef方法。

我确实有类似的问题,我有一个包含大量数据的组件,并且在每个更改检测周期都不能重新检查它们。但是,当我们从网址中查看某些属性时,我们会相应地更改视图中的内容,使用onPush我们的视图不会自动刷新。

因此,在构造函数中,使用 DI 来获取changeDetectorRef的实例: constructor(private changeDetectorRef: ChangeDetectorRef)

无论您何时需要触发changeDetection: this.changeDetectorRef.markForCheck();

答案 2 :(得分:2)

我也遇到了类似问题,在哪里优化我的应用性能,我不得不使用 changeDetection.OnPush 策略。所以我将它注入我的父组件以及我的子组件的构造函数, changeDetectorRef

的实例
    export class Parentcomponent{
       prop1;

       constructor(private _cd : ChangeDetectorRef){
          }
       makeXHRCall(){
        prop1 = ....something new value with new reference;
        this._cd.markForCheck(); // To force angular to trigger its change detection now
       }
    }

同样在子组件中,注入了 changeDetectorRef

的实例
    export class ChildComponent{
     @Input myData: myData[];
     constructor(private _cd : ChangeDetectorRef){
          }
       changeInputVal(){
        this.myData = ....something new value with new reference;
        this._cd.markForCheck(); // To force angular to trigger its change detection now
       }
    }

角度变化检测在每个异步函数上触发: -

  • 任何DOM事件,例如点击,提交,鼠标悬停。
  • 任何XHR电话
  • 任何定时器,如setTimeout()等。

所以,这种减慢了app的速度,因为即使我们拖动鼠标,angular也会触发changeDetection。对于跨越多个组件的复杂应用程序,这可能是一个主要的性能瓶颈,因为angular有这种树的父级到子级更改检测策略。 为了避免这种情况,我们最好使用OnPush策略并强制触发角度变化检测,我们发现存在参考变化。

其次,在OnPush策略中,应该非常小心它只会在对象引用发生变化时触发变化,而不仅仅是对象属性值,即Angular变更“意味着”新引用“。

例如: -

    obj = { a:'value1', b:'value2'}'
    obj.a = value3;

'obj'中'a'的属性值可能有变化,但是obj仍然指向相同的引用,因此Angular变化检测不会在此触发(除非你强制它); 要创建新引用,需要将对象克隆到另一个对象中并相应地分配其属性。

为了进一步理解,请阅读不可变数据结构,更改检测here

答案 3 :(得分:0)

最新答案,但另一个解决方法是通过在突变后使用传播算子。 myArr = [...myArr]myObj = {...myObj}

这甚至可以在变异时完成:myArr = myMutatingArr([...myArr]),因为参数被当作Array的新引用,使变量成为新引用,因此调用了Angular检查。

如前所述,如果更改参考,将进行检查,在任何情况下都可以使用散布运算符来做到这一点。

请警惕,尽管数据结构内部的嵌套数据结构需要将引用更改到嵌套级别。您必须进行一次迭代,以在价差内返回价差,例如:

myObj = {...myObj, propToChange: { ...myObj.propToChange, nestedPropArr: [ ...myObj.propToChange.nestedPropArr ] } }

,如果您需要对对象等进行迭代,则可能会变得很复杂。希望这对某人有帮助!