Angular变更检测未被文档记录的行为求证及原理问询
Angular变更检测双阶段机制验证与行为解释
你的推测完全正确——Angular变更检测确实存在标记阶段和执行阶段两个核心环节,这是官方文档未详细披露但源码层面明确实现的逻辑,以下结合你的实验现象逐一拆解:
核心机制说明
1. 标记阶段(Marking Phase)
当组件事件触发、输入属性变更或调用markForCheck()时,Angular会从触发点组件开始,向上遍历至根组件:
- 对非OnPush组件:自动标记为待检测状态(更新视图的
checksEnabled标记); - 对OnPush组件:仅在自身事件触发、输入引用变更或手动调用
markForCheck()时,才会被标记,同时向上标记所有祖先OnPush组件的路径,确保执行阶段能遍历到。
2. 执行阶段(Execution Phase)
从根组件开始,自上而下遍历所有被标记的组件,执行实际的变更检测逻辑(对比绑定值、更新DOM)。若某个组件处于detached状态,该组件及其未被单独标记的子组件会被跳过。
对应你的实验现象解释
- 手动触发父组件
detectChanges(),子组件无变化:此时子组件未被标记(无事件触发、输入未变更),执行阶段不会处理该子组件。 - 子组件触发事件但无变更检测:事件触发后,子组件会被标记,但默认的全局变更检测(由Zone.js触发)从根组件开始遍历,父组件处于
detached状态导致整个分支被跳过,子组件的检测逻辑未被执行。 - 先触发子组件事件再调用父组件
detectChanges():子组件已被标记,手动调用父组件的detectChanges()会从父组件开始向下遍历,此时子组件的标记状态生效,因此会执行变更检测。
与markForCheck()的逻辑呼应
markForCheck()的核心逻辑就是手动触发标记阶段:从当前组件向上遍历至根,标记所有OnPush组件的路径为待检测状态,确保后续执行阶段能覆盖到该组件,这和你观察到的事件触发标记逻辑完全一致。
这个双阶段机制可以在Angular源码的View类及ChangeDetectorRef的实现中找到细节,比如markViewForCheck()、detectChanges()方法的内部处理流程。
内容的提问来源于stack exchange,提问作者M G
相关产品推荐
相关产品推荐

