开启掘金成长之旅!这是我参与「掘金日新计划 · 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,那他们的关系是不是已经非常清楚了呢。