为什么 Angular 在 DOM 事件的 parent 组件上触发 ChangeDetection 尽管有 OnPush 策略?

Why does Angular trigger ChangeDetection on parent components on DOM events despite OnPush strategy?

技巧问题我还没有在文档中找到任何答案。

OnPush 策略已知会在以下情况下触发 ChangeDetection :

  1. 当接收到组件侦听的 DOM 事件时
  2. |async 管道接收到新事件时
  3. @Input()
  4. 当显式调用 ChangeDetectorRef::markForCheck 时(或任何其他类似方法,如 ApplicationRef::tickChangeDetectorRef::detectChanges

所以当一个组件触发了一个DOM事件,它不会影响兄弟组件的CD。 但是它将在他的层次结构中的每个 parent 组件上触发,无论是 Default 还是 OnPush。这在调用 detectChanges 时不会发生,例如只有组件获得 CD。

我还注意到,ApplicationRef.tick() 只会在 non-OnPush 个组件上触发 CD。

为什么 OnPush 不提供完全隔离并仍然在 parent 秒时触发 CD?

请看一个working illustration on stackblitz

也许你对OnPush的误解和我刚开始学习变化检测的时候一样:)。重点是:OnPush 策略不会改变任何关于触发 CD 周期的事件。这只是 taken care of by zone.js by patching browser APIs. (Unless CD is triggered manually.) zone.js does not care about components and ChangeDetectionStrategies. And CD always starts at the root as @Chris Hamilton 解释。

ChangeDetectionStrategy.Default 和 OnPush 的区别仅在于组件将在 CD 周期中实际被检查。

所以你原 post 中的句子应该是: “已知 OnPush 策略仅在以下情况下标记要检查的组件”。

请参阅下面的示例,这是我使用 Angular DevTools 的分析器观察到的。我在OnPush组件的ngOnInit中放了一个setTimeout(() => {}, 6000),它触发了一个CD循环(来源是“setTimeout”),但并没有导致组件本身被检查。

关于手动触发的detectChanges()、tick()和markForCheck()的区别:

  • ChangeDetectorReference.detectChanges() 通过尊重子组件的 CD 策略在该组件及其子组件上触发 CD
  • ApplicationRef.tick() 遵循 CD 策略
  • 触发整个应用程序的 CD
  • ChangeDetectorReference.markForCheck()不触发CD,但标记所有OnPush父级在当前或下一个CD周期检查一次

(解释得很好here