源码角度理解 Angular 变更检测

声明

本文作为 Angular 变更检测原理源码级的探索文章,涉及到了 View 、Zone.js 这两个重要问题。建议对此不熟悉的读者翻看我之前的两篇文章:Angular 中 View 的那些事儿Zone.js 入门

前言

Angular 作为一个 MVVM 框架,由 View Model 层与 View 层配合实现数据绑定。具体来说,在 Angular 中,NgZone(Zone.js 的封装)负责劫持了所有的 Web 事件和异步函数(比如定时器等),并在其回调函数执行完毕后通知 Angular 对 View 树进行变更检测操作。变更检测通过比对 View 中绑定数据在事件前后的变化,来决定是否需要通过操作 DOM 的方式来更改页面上的绑定数据。一个简单的流程如下图所示。在下一节,我会从源码角度分析变更检测的原理。

关键流程

当 Angular 执行完 Web 事件或者定时器的回调任务后,会利用 EventEmitter通知应用。然后,应用会遍历依附( Attach ) 在 View 上的视图,依次执行变更检测操作。

为了针对不同的编译环境产生不同的底层函数,Angular 利用一个服务来完成时间检查更新视图的操作。在调试模式下,具体的检查视图函数最终会交由
debugCheckNoChangesView 处理,这个函数包含了许多调试需要的信息,可以方便我们定位调试。然后会进入真正的 checkAndUpdateView 中。

化繁为简

变更检测的简单流程图如上所示。 在 checkAndUpdateView 执行时,View 上指令嵌入式视图 、特殊配置下的 ViewChild*/ContentChild* 绑定的变量和页面上的插值都会进行检查并更新。在这五个类目中,插值的更新是最容易研究的。本文也从插值入手,研究变更检测最简单的对象。

插值更新

插值的变更检测最终交由 checkAndUpdateNode 执行。在若一个结点上插值数量小于10,那么编译器会执行 checkAndUpdateNodeInline 来提高性能。否则会使用 checkAndUpdateNodeDynamic 来对插值进行变更检测操作。两者实现方法略有差异。

前者使用 checkAndUpdateTextInline 来检查插值是否发生了变化。若插值发生了变化,则重新组装结点上的插值,最后通过修改 DOM 结点的 nodeValue 属性完成插值的更新。

后者使用 checkAndUpdateTextDynamic。这个方法不过是利用循环来迭代结点中的插值来进行更新。

例子

本文所有的调试都由这个基础的例子调试得来。

参考链接

Angular View 源码

发表评论

电子邮件地址不会被公开。 必填项已用*标注