1 变更检测
1.1 变更检测的概念
负责检测Angular组件中数据的变化,实时地更新视图。
1.2 变更检测的触发时机
Angular
会在发生异步操作(这些操作常常会引起数据改变)后,触发变更检测,实时地更新视图。
常见的异步操作有以下几种
- HTTP请求:通过Ajax请求或Fetch API 发送HTTP请求在后台获取数据
- 定时器任务:setTimeout/setInterval
- 事件处理:用户交互事件(点击、滚动、键盘输入等)
- 自定义的异步函数
1.3 变更检测是如何工作的(待补充)
基于zone.js
实现,,它能捕获异步操作。告诉Angular应用的状态可能已经改变了,在适当的时候触发变更检测机制。
1.4 变更检测的执行机制和顺序
只要有异步操作,就会触发变更检测。从根节点开始,从上到下遍历所有子组件(深度遍历)检测,直至最后一个组件达到稳定状态。
- 更新组件的输入属性
- 调用组件的生命周期的钩子函数
OnChanges
、OnInit
、DoCheck
、AfterContentInit
。` - 更新当前组件的视图DOM
- 递归更新子组件…:当前组件的视图更新完成后,就会递归地更新子组件的视图。包含了以上三个步骤。
- 调用所有子组件生命周期的钩子
ngAfterViewInit
1.5 常见的错误
如果违背了变更检测执行顺序,那么就会报错NG0100: ExpressionChangedAfterItHasBeenCheckedError
。后果的例子待补充(比如:在子组件生命周期中,修改了父组件模板中使用到的值(包括子组件绑定的属性、模板使用到的<p>{{ name }}</p>
))。
2 Angular的两种变更检测策略
CD:ChangeDetection
整个Angular应用是组件树
2.1 默认的脏检查Default策略
只要有异步操作,就会触发变更检测。从根节点开始,从上到下遍历所有子组件(深度遍历)检测,直至最后一个组件达到稳定状态。
2.2 OnPush策略
若为组件设置了OnPush
策略,Angular的变更检测会跳过该组件及其子组件的检测。只有以下几种情况会触发该组件的变更检测:
- 只有输入数据的引用(
@Input
)改变时,组件才进行变更检测 - 当
DOM事件
触发时,才进行变更检测:从根组件开始往下遍历进行变更检测 手动触发
变更检测(ChangeDetectorRef对象.detectChanges()
):从该组件开始往下遍历进行变更检测
3 使用Angular的变更检测器手动管理变更检测
3.1 ApplicationRef
3.1.1 概念
在我们创建了Angular应用
后,Angular
会创建一个ApplicationRef
的实例。它该应用根组件的引用
。主要负责启动应用、处理全局的变更检测和应用关闭时执行清理工作
3.1.2 ApplicationRef对象的常用函数
bootstrap()
:启动应用将根组件挂载到DOM中tick()
:手动触发一次变更检测- 该实例监听
NgZone
的onTurnDone
事件。当该事件触发时,执行tick()
触发变更检测
- 该实例监听
// 真实源码的非常简化版本。
class ApplicationRef {
changeDetectorRefs:ChangeDetectorRef[] = [];
constructor(private zone: NgZone) {
this.zone.onTurnDone
.subscribe(() => this.zone.run(() => this.tick());
}
tick() {
this.changeDetectorRefs
.forEach((ref) => ref.detectChanges());
}
}
attachView()
和detachView()
:手动将视图附加到或分离出应用中isStable:Observable<boolean>()
:检测应用是否稳定
3.2 ChangeDetectorRef:组件级别的变更检测器
3.2.1 概念
Angular会为每个组件实例生成一个变更检测器,用于管理变更检测。
我们可以通过注入对象来获得
3.2.2 ChangeDetectorRef对象的常用函数
detectChanges()
: 手动触发1次变更检测markForCheck()
: 标记当前组件及其父组件为脏检测状态,但不立即执行变更检测,等待下一轮变更检测时执行detach()
: 从变更检测树中分离当前组件,即暂时停止对当前组件的变更检测reattach()
: 重新将当前组件添加到变更检测树中,恢复对当前组件的变更检测run()
: 在 Zone 外部运行变更检测,可以用于优化性能
4 不必要的变更检测(待补充)
4.1 zone pollutions
Polluting the zone happens when we run an initialization logic that uses requestAnimationFrame, setTimeout or addEventListener.
comes from 3p libraries with triggers
move initializations outside the Angular zone