我有一个数据表组件(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
没有触发行单元格值的变化检测,实现这一目标的最佳方法是什么?
答案 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
}
}
答案 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
}
}
角度变化检测在每个异步函数上触发: -
所以,这种减慢了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 ]
}
}
,如果您需要对对象等进行迭代,则可能会变得很复杂。希望这对某人有帮助!