Angular 变化检测:为什么要检查整棵树的局部变化?

Angular change detection: why is entire tree checked on local changes?

假设我有一个简单的文本数组:

textContent = [
    {text: 'good morning'},
    {text: 'lovely day'},
    {text: 'for debugging'},
  ]

我为每个渲染一个简单的组件,将文本项作为输入传递:

<app-text *ngFor="let text of textContent" [text]="text"></app-text>

文本组件渲染文本,使用 ngModel:

<textarea [(ngModel)]="text.text"></textarea>

我也在登录 DoCheck:

ngDoCheck() {
    console.log(this.text.text, ' has been checked')
}

当我在 one textarea 中输入内容时,所有其他 组件的 DoCheck 被触发。

good morning   has been checked
lovely days    has been checked
for debuggings has been checked

即使将 OnPush 用于更改检测,也会发生这种情况。

问题:为什么对一个组件的数据所做的更改会触发整个树的检查?

(少数组件不是问题,但如果树较大,我会丢帧)。

我实际上注意到,即使是完全独立的内容树上的组件也会触发 'doCheck'。

有没有办法防止这种情况发生,还是我完全遗漏了其他东西?

在 Angular 上,每个组件都有自己的变化检测器。这背后的原因是允许我们单独控制每个组件。有了这个意味着我们可以完全控制整个应用程序的每个组件何时发生变化检测。

虽然这是一个很棒的功能,但是当您将 Angular 应用程序视为一组组件或通常称为组件树的东西时,这意味着树中的所有组件都将具有相同的功能。他们自己的个人变更检测器机制。当你把它们放在一起时,你基本上让整棵树执行变化检测。

如果没有设置高级技术或配置,Angular 默认情况下使用保守的方法并在发生更改时触发对整个树的更改检测。

当然,有一些机制和技术可以让您对此有更多的控制,并且只有当某些东西实际发生变化并且仅针对特定组件和子组件时才会发生变化检测。

看看这个 article,Pascal Precht 详细解释了变化检测的工作原理。

有一些技术可以避免您遇到的问题,例如您提到的 onPush 变化检测,以及不可变对象和可观察对象的使用。如果你想实现对变化检测的完全控制,你可以将所有组件设置为具有 onPush 策略。

我的问题是基于我的误解,所以让我澄清一下:

当一个组件的 DoCheck() 生命周期钩子被调用时,并不意味着它已经被检测到变化。这仅表示 Angular 正在执行更改检测 运行,并为您提供机会挂钩它以执行一些自定义逻辑。

这篇文章 If you think ngDoCheck means your component is being checked — read this article 有更多详细信息。