Angular变更检测策略非常的强大,智能,以至于也带来一些痛点。检测策略会很容易被触发,例如: 点击事件,异步事件(用户交互,XHR)等。 组件中生效,但其他相邻兄弟组件不会被监测。这样就大大提升了响应速度,以免造成页面不必要的卡顿。
import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core';
@Component({
changeDetection: ChangeDetectionStrategy.OnPush
})
export declare enum ChangeDetectionStrategy {
/**
* Use the `CheckOnce` strategy, meaning that automatic change detection is deactivated
* until reactivated by setting the strategy to `Default` (`CheckAlways`).
* Change detection can still be explicitly invoked.
* This strategy applies to all child directives and cannot be overridden.
*/
OnPush = 0,
/**
* Use the default `CheckAlways` strategy, in which change detection is automatic until
* explicitly deactivated.
*/
Default = 1
}
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.
*/
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;
}
markForCheck()
当视图使用 OnPush(checkOnce)变更检测策略时,把该视图显式标记为已更改,以便它再次进行检查。
使用markForCheck时会标记当前节点及其父节点,爷节点一直到根节点为dirty状态,这样当有操作触发更新时,比如按钮点击,定时器等操作时会触发ngZone的事件拦截,触发applicationRef 的tick()方法进行全局的更新(组件标记为Onpush时只更新dirty状态的组件)。
markForCheck() {
markViewDirty(this._cdRefInjectingView || this._lView);
}
function markViewDirty(lView) {
while (lView) {
lView[FLAGS] |= 64 /* Dirty */;
const parent = getLViewParent(lView);
// Stop traversing up as soon as you find a root view that wasn't attached to any container
if (isRootView(lView) && !parent) {
return lView;
}
// continue otherwise
lView = parent;
}
return null;
}
detach()
从变更检测树中分离开视图。 已分离的视图在重新附加上去之前不会被检查。 与 detectChanges() 结合使用,可以实现局部变更检测。
detach会改变当前视图的标志,当组件树进行更新时,就算当前组件是dirty也不会触发更新。
detach() {
this._lView[FLAGS] &= ~128 /* Attached */;
}
detectChanges()
检查该视图及其子视图。与 detach 结合使用可以实现局部变更检测。
detectChanges() {
detectChangesInternal(this._lView[TVIEW], this._lView, this.context);
}
function detectChangesInternal(tView, lView, context) {
const rendererFactory = lView[RENDERER_FACTORY];
if (rendererFactory.begin)
rendererFactory.begin();
try {
refreshView(tView, lView, tView.template, context);
}
catch (error) {
handleError(lView, error);
throw error;
}
finally {
if (rendererFactory.end)
rendererFactory.end();
}
}
相当于调用refreshView()刷新当前视图及其子视图。
reattach()
把以前分离开的视图重新附加到变更检测树上。 视图会被默认附加到这棵树上。
reattach() {
this._lView[FLAGS] |= 128 /* Attached */;
}