# 跳过组件子树

默认情况下,JavaScript 会使用你可以从多个不同组件引用的可变数据结构。Angular 会在你的整个组件树上运行变更检测,以确保数据结构的最新状态反映在 DOM 中。

对于大多数应用程序,变更检测都足够快。但是,当应用程序有特别大的组件树时,在整个应用程序中运行变更检测可能会导致性能问题。你可以通过将变更检测配置为仅在组件树的子集上运行来解决这个问题。

如果你确信应用程序的一部分不受状态更改的影响,可以用 OnPush 跳过整个组件子树中的变更检测。

# 使用 OnPush

OnPush 变更检测会指示 Angular 仅在以下情况下为组件子树运行变更检测:

  • 子树的根组件接收到作为模板绑定的结果的新输入。Angular 将输入的当前值和过去值使用 ==进行比较

  • Angular 在本子树的根组件或它的任何子组件(而不管其是否使用 OnPush 变更检测方式)中处理使用 OnPush 变更检测策略的组件中的事件(比如事件绑定、输出绑定或 @HostListener)时

你可以在 @Component装饰器中将组件的变更检测策略设置为 OnPush

import { ChangeDetectionStrategy, Component } from '@angular/core';
@Component({
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class MyComponent {}

# 常见的变更检测场景

本节展示了几种常见的变更检测场景,以说明 Angular 的行为。

# 事件由具有默认变更检测的组件处理

如果 Angular 在没有 OnPush策略的情况下处理组件中的事件,则框架会在整个组件树上执行变更检测。Angular 将跳过具有 OnPush策略的组件的后代组件子树,如果该组件没有收到新输入的话。

比如,如果我们将 MainComponent的变更检测策略设置为 OnPush,并且用户与具有根 MainComponent的子树外的组件交互,Angular 将检查下图中的所有绿色组件(AppComponentHeaderComponentSearchComponentButtonComponent),除非 MainComponent接收到了新的输入:

Change detection propagation from non-OnPush component

# 事件由具有 OnPush 的组件处理

如果 Angular 使用 OnPush 策略处理组件中的事件,则框架将在整个组件树中执行变更检测。Angular 将忽略以具有 OnPush 策略的组件为根的组件子树(如果这个根组件尚未接收到新输入并且在处理此事件的组件外部)。

比如,如果 Angular 处理 MainComponent中的事件,则框架将在整个组件树中运行变更检测。Angular 将忽略具有根 LoginComponent的子树,因为该组件具有 OnPush策略并且此事件发生在其范围之外。

Change detection propagation from OnPush component

# 事件由具有 OnPush 的组件的后代处理

如果 Angular 使用 OnPush 处理组件中的事件,则框架将在整个组件树中执行变更检测,包括组件的祖先。

比如,在下图中,Angular 会处理使用 OnPush 的 LoginComponent中的事件。Angular 将在整个组件子树中调用变更检测,包括 MainComponentLoginComponent的父级),尽管 MainComponent也有 OnPush。Angular 也会检查 MainComponent,因为 LoginComponent是其视图的一部分。

Change detection propagation from nested OnPush component

# 具有 OnPush 策略的组件的新输入

Angular 将在具有 OnPush策略的子组件中运行变更检测,将 input 属性设置为模板绑定的结果。

比如,在下图中,AppComponent会将新输入传递给 MainComponent,它具有 OnPush策略。Angular 将在 MainComponent中运行变更检测,但不会在同样具有 OnPush策略的 LoginComponent中运行变更检测,除非它也接收到新的输入。

Change detection propagation with OnPush component that receives new inputs

# 边缘情况

  • 修改 TypeScript 代码中的输入属性。当你使用 @ViewChild@ContentChild等 API 来获取对 TypeScript 中组件的引用并手动修改 @Input属性时,Angular 将不会自动为 OnPush 组件运行变更检测。如果你需要 Angular 运行变更检测,你可以在你的组件中注入 ChangeDetectorRef并调用 changeDetectorRef.markForCheck()来告诉 Angular 为其安排一次变更检测。

  • 修改对象引用。如果输入接收到可变对象作为值,并且你修改了对象内容但引用没变,则 Angular 将不会调用变更检测。这是预期的行为,因为输入的前一个值和当前值都指向了同一个引用。

Last Updated: 5/5/2023, 9:06:42 AM