【问题标题】:Performance implications of binding to simple fields vs. properties of objects vs. getters in Angular绑定到简单字段与对象属性与 Angular 中的 getter 的性能影响
【发布时间】:2018-07-09 04:46:59
【问题描述】:

与绑定到对象的属性相比,绑定到简单字段(例如[(ngModel)]="room")时的性能有什么不同吗? (例如[(ngModel)]="lesson.room")如果是,为什么?

关于 getter:我对绑定到 getter/setter 的理解是否正确,例如单向绑定到函数是不好的,因为该函数将在每个检测周期被调用,而 Angular 无法区分没有变化的情况和实际发生变化并且实际上需要更新视图的情况?这应该使 getter 在这方面成为性能最低的选择。

【问题讨论】:

  • 如果您有多个问题,请在单独的线程中提问。
  • 这些问题紧密相连,我无法证明单独提出一个问题@KrishnaMohan

标签: angular data-binding binding


【解决方案1】:

至少在 lesson 引用更改之前,性能应该没有差异。即使那样,差异也不存在,因为 JavaScript 中的属性访问非常快。需要注意的是room也是一个属性。

如果访问器方法或属性 getter 被绑定,它将在每个更改检测周期被调用。如果函数调用成本不高和/或更改检测周期不太密集,这可能是合适的。如果它们很密集,可以使用OnPush 变化检测策略减少它们的数量。

比如{{ foobar }}绑定就可以了:

get foobar() {
  return this.foo + this.bar;
}

{{ factorial(num) }} 绑定可能代价高昂,它可以重构为纯管道或缓存到num 更改时的属性。

应该注意可能有一些警告,例如如果没有在原型上定义属性 getter 将遭受 performance penalty in Chrome/V8 的影响,例如:

lesson = { get room() {/*...*/} }

根据实际性能影响,这些问题可能被视为初步优化。

【讨论】:

  • 您在谈论“密集”变化检测周期。有没有办法在不改变变化检测策略(或分离变化检测器)的情况下影响密度?据我了解,Angular 决定何时运行更改检测:x 秒后、键入、单击等时。
  • 变化检测周期发生在当前区域触发变化检测时。是的,它将由 DOM 或其他异步事件触发(只要它们发生在区域中)。可以使用区域runOutsideAngular 限制/去抖动或评估负责 CD 垃圾邮件的代码片段。 CD 也可以(暂时)通过 ChangeDetectorRef detach 关闭。
  • 注意 Angular 事件绑定触发 CD,例如(mousemove)=... 是一个著名的垃圾邮件发送者。因此,它应该使用detach 处理,或者必须手动设置事件侦听器并加以限制,例如Observable.fromEvent(...).debounceTime(...)。同样,这是初步优化,除非另有证明。
  • 我正在使用@HostListener('document:keydown', ['$event']) 并注意到这可能是我的性能问题的罪魁祸首。显然,从 HostListener 事件触发更改检测既不受分离更改检测器的影响,也不受将相关组件的检测策略设置为 OnPush 的影响。您可以给我任何提示(暂时)避免触发更改检测的 HostListener 事件?
  • 我明白了。是的,HostListener 很可能是个问题。这与上面的建议相同,您必须使用Observable.fromEvent(document, 'keydown') 手动设置一个侦听器并使用 runOutsideAngular 去抖动或禁用 CD。 document 可用于 DI 作为 DOCUMENT 提供者。
【解决方案2】:

两者略有不同

[(ngModel)]="room"

[(ngModel)]="lesson.room")

因为在前一种情况下,更改检测仅在房间与上次检查时的实例相同时检查,并且 在后一种情况下,它必须检查lession,然后检查room

如果lession 是一个对象而room 是一个普通属性,这并不重要,因为这样的检查非常快。

函数调用的性能不如简单的对象标识比较,但也不算太糟糕。这取决于更改检测发生的频率。 最重要的部分是

  • 该函数在后续调用中返回相同的值(如果两者之间没有异步执行),否则 Angular 会识别这一点并抛出臭名昭著的“自上次检查以来表达式已更改”

  • 该函数本身不会做昂贵的工作。

如果这些标准被认为绑定到函数在某些情况下是可以接受的。它很容易出错,因此在您完全了解变更检测的工作原理之前,通常最好不要使用它。

确定它是否适合您的最佳方法是为您的具体用例创建自己的基准。

【讨论】:

  • 您是在说“这取决于更改检测发生的频率”。如果不更改变更检测周期(或拆下变更检测器),就无法影响变更检测周期的数量,可以吗?因此,如果我理解正确,使用 getter/functions 的开销是函数调用本身的开销加上函数所做的任何工作/计算?然后,此开销应该与每个检测周期相关。
  • “不改变变更检测周期”应该是“不改变变更检测策略”
  • 如果没有更改传播到子组件,则不会有更改。例如,您在child 组件中有@Input() a:string; b:string;<child [prop]="b"></child>ChangeDetectionStrategy.OnPush,那么子组件中的更改检测将仅在您更改b 时运行。如果将父项中的输入更改为 ``@Input() 设置 a(value:string) { if(value.prop this.b.prop) { this.b = value; this.cdRef.detectChanges(); };` 那么<child> 上的更改检测将仅在value.prop differs from this.b.prop` (或其他导致更改的事件...
  • ... 在带有OnPush 的组件中检测。
  • 那么更改变更检测策略有什么问题?
猜你喜欢
  • 2018-08-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-29
  • 2021-05-04
  • 2017-03-27
  • 2018-04-25
  • 1970-01-01
相关资源
最近更新 更多