14

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

实施新的变更检测策略后,当数据对象发生突变(例如对象的属性更新)时,引用表未更新的错误被提交参考:https ://github.com/swimlane/angular2-data-table/issues/ 255 . 可以为这种类型的需求创建一个强大的用例,例如在线编辑或对大型数据集合(如股票代码)中的单个属性进行外部数据更改。

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

在实时重新加载下的演示页面上,您可以看到上述提交中引用的演示正在运行,但在您单击之前不会更新表,从而触发更改检测。

组件的结构类似于:

Table > Body > Row Group > Row > Cell

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

我们希望OnPush为那些实现这种模式的人提供变更检测,但是,作为一个拥有多个消费者的开源项目,人们可能会争论某种自定义检查功能,以检查屏幕上的可见行值。

综上所述,trackBy没有触发行单元格值中的更改检测,实现此目的的最佳方法是什么?

4

4 回答 4

25

Angular2 更改检测不检查数组或对象的内容。

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

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

这种方式this.myArray引用了一个不同的数组实例,Angular 会识别出这个变化。

另一种方法是使用IterableDiffer(for arrays) 或KeyValueDiffer(for objects)

// 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

于 2016-11-27T14:25:02.620 回答
3

您可能想使用markForCheck方法 from ChangeDetectorRef


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

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

在任何需要触发 changeDetection 的地方: this.changeDetectorRef.markForCheck();

于 2016-11-27T14:34:57.220 回答
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() 等。

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

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

例如:-

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

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

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

于 2017-04-23T09:40:22.313 回答
0

迟到的答案,但另一种解决方法是在突变后使用扩展运算符.. myArr = [...myArr]myObj = {...myObj}

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

如前所述,如果您更改引用,则会进行检查,在任何情况下都可以使用扩展运算符来做到这一点。

尽管数据结构中的嵌套数据结构需要将引用更改为嵌套级别,但要小心。您将不得不进行一次迭代,以在价差内返回价差,如下所示:

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

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

于 2019-01-09T10:06:14.950 回答