# 跳过组件子树
默认情况下,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 将检查下图中的所有绿色组件(AppComponent
、 HeaderComponent
、 SearchComponent
、 ButtonComponent
),除非 MainComponent
接收到了新的输入:
# 事件由具有 OnPush 的组件处理
如果 Angular 使用 OnPush 策略处理组件中的事件,则框架将在整个组件树中执行变更检测。Angular 将忽略以具有 OnPush 策略的组件为根的组件子树(如果这个根组件尚未接收到新输入并且在处理此事件的组件外部)。
比如,如果 Angular 处理 MainComponent
中的事件,则框架将在整个组件树中运行变更检测。Angular 将忽略具有根 LoginComponent
的子树,因为该组件具有 OnPush
策略并且此事件发生在其范围之外。
# 事件由具有 OnPush 的组件的后代处理
如果 Angular 使用 OnPush 处理组件中的事件,则框架将在整个组件树中执行变更检测,包括组件的祖先。
比如,在下图中,Angular 会处理使用 OnPush 的 LoginComponent
中的事件。Angular 将在整个组件子树中调用变更检测,包括 MainComponent
(LoginComponent
的父级),尽管 MainComponent
也有 OnPush
。Angular 也会检查 MainComponent
,因为 LoginComponent
是其视图的一部分。
# 具有 OnPush 策略的组件的新输入
Angular 将在具有 OnPush
策略的子组件中运行变更检测,将 input 属性设置为模板绑定的结果。
比如,在下图中,AppComponent
会将新输入传递给 MainComponent
,它具有 OnPush
策略。Angular 将在 MainComponent
中运行变更检测,但不会在同样具有 OnPush
策略的 LoginComponent
中运行变更检测,除非它也接收到新的输入。
# 边缘情况
修改 TypeScript 代码中的输入属性。当你使用
@ViewChild
或@ContentChild
等 API 来获取对 TypeScript 中组件的引用并手动修改@Input
属性时,Angular 将不会自动为 OnPush 组件运行变更检测。如果你需要 Angular 运行变更检测,你可以在你的组件中注入ChangeDetectorRef
并调用changeDetectorRef.markForCheck()
来告诉 Angular 为其安排一次变更检测。修改对象引用。如果输入接收到可变对象作为值,并且你修改了对象内容但引用没变,则 Angular 将不会调用变更检测。这是预期的行为,因为输入的前一个值和当前值都指向了同一个引用。