为什么 Angular 异步管道使用 cdr.markForCheck() 而不是 cdr.detectChanges()?

Why does Angular async pipe uses cdr.markForCheck() and not cdr.detectChanges()?

我有一个一般的Angular问题:

为什么Angular async管道使用cdr.markForCheck()而不是cdr.detectChanges()

我看到的这两个有两个主要区别'styles':

  1. markForCheck() 标记要检查到根组件的路径 - 要更新的内容
  2. markForCheck() 让变化检测发生在当前或下一个周期 - timing

我的想法或问题:

  1. 为什么我们需要检查到根的整个路径(在 async 管道中)?为什么不只是当前组件? (detectChanges()) - 这与要更新的内容

    有关
  2. 为什么只标记(对于 current/next 循环 - 在 markForCheck() 中使用 ngZone)?为什么不立即检测变化? (detectChanges()) 这与时间

    有关
  3. 如果没有 ngZone 异步触发器/没有异步操作怎么办?那么视图更新不会发生吗?

  4. 如果我们将 async 管道改为使用 detectChanges() 会发生什么情况?


async pipe:

  private _updateLatestValue(async: any, value: Object): void {
    if (async === this._obj) {
      this._latestValue = value;
      this._ref.markForCheck();
    }
  }

编辑:

请不要解释每个方法的作用,因为我在文档中多次阅读它,从 async 的角度理解我无法理解。 我要知道的重要部分是为什么关于 更新内容 时间 .

为了正确的文档检查 ChangeDetectorRef

检测变化

Checks this view and its children

如果您的 class 已更改但视图尚未更新,您需要通知 Angular 以检测这些更改。

markForCheck

When a view uses the OnPush (checkOnce) change detection strategy, explicitly marks the view as changed so that it can be checked again.

仅从文本中您已经可以看出,此策略用于组件检测策略已更改为 onPush 的情况(例如,如果 @Input() 已更改)。

更新

直接回答您的问题:

  1. why do we need to check whole path to the root (in async pipe)? Why not just current component? (detectChanges()) - this relates to what to update

它不检查根,而是检查组件的祖先

  1. why just mark only (for current/next cycle - in markForCheck() using ngZone)? Why not detect changes immedially? (detectChanges()) this relates to timing

我认为这是与性能相关的主题。收集所有需要运行的检查并在下一个周期执行它们。

  1. what if there is no ngZone async trigger / no async operation? Then the view update won't happen?

正确,如果Angular不会收到通知,那么视图中不会发生任何变化

  1. what would happen if we changed async pipe to use detectChanges() instead?

我想它也可以工作,但它不会只检查更改,而是直接执行视图更新。

As Angular doc says:

detectChanges() 是:

Checks this view and its children. Use in combination with detach to implement local change detection checks.

markForCheck() 是:

When a view uses the OnPush (checkOnce) change detection strategy, explicitly marks the view as changed so that it can be checked again.

因此 detectChanges() 运行 立即更改检测,但是 markForCheck() 不会 运行 更改检测。

所以看起来 async 管道使用 markForCheck 来避免性能问题。我的意思是在 async 管道中使用 markForcheck() 有助于避免 运行 不必要的更改检测。

更新:

问题 1:

如果没有正在进行的当前变化检测并且没有未来 CD 的 ngZone 异步触发器怎么办?

不,视图不会更新,但是,它将标记从当前组件到根组件的所有组件,它们的状态是CheckEnabledThe source code:

export function markParentViewsForCheck(view: ViewData) {
    let currView: ViewData|null = view;
    while (currView) {
        if (currView.def.flags & ViewFlags.OnPush) {
           currView.state |= ViewState.ChecksEnabled;
        }
    currView = currView.viewContainerParent || currView.parent;
    }
}

问题 2:

关于要更新的​​内容,为什么我需要标记到根目录的整个路径? ...在第二个解决方案 (detectChanges) 中不是吗?

请阅读这个很好的答案 。总之还是用markForCheck()的方法比较好