详解Angular中的变更检测(六)- 组件的ChangeDetector

238 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第4天,点击查看活动详情

变更检测如何工作

Angular 的核心是 组件化,组件最终形成一棵组件树。Angular 在生成组件的同时,也会为每一个组件生成一个变更检测器changeDetector,用来记录组件的数据变化状态,一个 Component 会对应一个changeDetector,所以changeDetector同样也是一个树状结构的组织。

从上面我们知道,Angular中每个组件都有自己的变更检测器ChangeDetector,正是由于有了这个变更检测器,我们可以对每个组件分别控制如何以及何时进行变更检测。

在组件中我们可以通过注入 ChangeDetectorRef 来获取组件的 changeDetector

@Component({
 selector: "todos",
 ...
})
export class TodosComponent{
 constructor(private _cd: ChangeDetectorRef) {}
}

ChangeDetectorRef

ChangeDetectorRef也就是,变更检测对象引用。先来看一下这个对象都有哪些方法吧。

export declare abstract class ChangeDetectorRef {
    /**
     * When a view uses the {@link ChangeDetectionStrategy#OnPush OnPush} (checkOnce)
     * change detection strategy, explicitly marks the view as changed so that
     * it can be checked again.
     *
     * Components are normally marked as dirty (in need of rerendering) when inputs
     * have changed or events have fired in the view. Call this method to ensure that
     * a component is checked even if these triggers have not occured.
     *
     * <!-- TODO: Add a link to a chapter on OnPush components -->
     *
     */
    abstract markForCheck(): void;
    /**
     * Detaches this view from the change-detection tree.
     * A detached view is  not checked until it is reattached.
     * Use in combination with `detectChanges()` to implement local change detection checks.
     *
     * Detached views are not checked during change detection runs until they are
     * re-attached, even if they are marked as dirty.
     *
     * <!-- TODO: Add a link to a chapter on detach/reattach/local digest -->
     * <!-- TODO: Add a live demo once ref.detectChanges is merged into master -->
     *
     */
    abstract detach(): void;
    /**
     * Checks this view and its children. Use in combination with {@link ChangeDetectorRef#detach
     * detach}
     * to implement local change detection checks.
     *
     * <!-- TODO: Add a link to a chapter on detach/reattach/local digest -->
     * <!-- TODO: Add a live demo once ref.detectChanges is merged into master -->
     *
     */
    abstract detectChanges(): void;
    /**
     * Checks the change detector and its children, and throws if any changes are detected.
     *
     * Use in development mode to verify that running change detection doesn't introduce
     * other changes. Calling it in production mode is a noop.
     */
    abstract checkNoChanges(): void;
    /**
     * Re-attaches the previously detached view to the change detection tree.
     * Views are attached to the tree by default.
     *
     * <!-- TODO: Add a link to a chapter on detach/reattach/local digest -->
     *
     */
    abstract reattach(): void;
}

这里我们先做简单的了解,在后续的文章中会对这个做详细的介绍。

除了ChangeDetector,我们还知道,在创建一个 Angular 应用 后,Angular 会同时创建一个 ApplicationRef 的实例,这个实例代表的就是我们当前创建的这个 Angular 应用的实例。ApplicationRef创建的同时,会订阅 ngZone 中的 onMicrotaskEmpty 事件,在所有的异步任务完成后调用所有的视图的 detectChanges() 来执行变化检测。

class ApplicationRef {
  // ViewRef 是继承于 ChangeDetectorRef 的
  _views: ViewRef[] = [];
  constructor(private _zone: NgZone) {
    this._zone.onMicrotaskEmpty.subscribe({
      next: () => {
        this._zone.run(() => {
          this.tick();
        });
      },
    });
  }

  // 执行变化检测
  tick() {
    for (let view of this._views) {
      view.detectChanges();
    }
  }
}

我们现在已经介绍了什么是组件的ChangeDetector,还有ApplicationRef,那他们的关系是不是已经非常清楚了呢。