在开始阅读源码之前,先了解一些必备的基础概念:AST 数据结构,VNode 数据结构, 的问题,render function。
的全称是 Abstract Syntax Tree(抽象语法树),是源代码的抽象语法结构的树状表现形式,计算机学科中编译原理的概念。Vue 源码中借鉴 jQuery 作者 的 对模板进行解析,得到的就是 AST 代码。
注意:为了避免文章过长,我在以上的代码中注释了 ASTElement 中的许多属性,点击上方 的链接可查看完整代码。
周公解梦和死人说话
VNode 是 VDOM 中的概念,是真实 DOM 元素的简化版,与真实 DOM 元素是一一对应的关系。
本文中我们关注代码中后面带注释的属性,后面的 render function 的生成跟这些属性相关。可在实际的 Vue 项目中加一个断点,查看实际的 VNode 中这些属性的值。
我们为什么不直接使用原生 DOM 元素,而是使用真实 DOM 元素的简化版 VNode,最大的原因就是 document. 这个方法创建的真实 DOM 元素会带来性能上的损失。我们来看一个 document. 方法的例子:
打开 console 运行一下的代码,你会发现打印出来的属性多达 228 个,而这些属性有 90% 多对我们来说都是无用的。VNode 就是简化版的真实 DOM 元素,只将我们需要的属性拿过来,并新增了一些在 diff 过程中需要使用的属性,例如 isStatic。
在上一篇博客中我们简单讲了 Vue 的生命周期,在 _init 函数的最后一步就是 $mount 方法。这个方法就是模板渲染的入口。我们看一下下面这张图:
上图中展示了模板渲染过程中涉及到的核心函数。我们可以通过 WebStrom 查看源码(按住 control 键单击方法名可以直接跳转,源码阅读神器),或者在浏览器中打断点一步一步查看代码运行的过程。
在这个函数中出现了熟悉的 new Watcher,这一部分在上一篇博客中详细介绍过,主要是将模板与数据建立联系,所以说 Watcher 是模板渲染和数据之间的纽带。
至此,模板解析完成,拿到了 render function,也通过 Watcher 与将之数据联系在一起。
函数(src/compiler/optimizer.js)主要功能就是标记静态节点,为后面 patch 过程中对比新旧 VNode 树形结构做优化。被标记为 static 的节点在后面的 diff 算法中会被直接忽略,不做详细的比较。
上一节我们提到了 __patch__ 函数最终会进入 。patch.js 就是新旧 VNode 对比的 diff 函数,diff 算法来源于 ,是 VDOM 思想的核心。对两个树结构进行完整的 diff 和 patch,复杂度增长为 O(n^3),而 snabbdom 的算法根据 DOM 操作跨层级增删节点较少的特点进行调整,将代码复杂度降到 O(n),算法比较如下图,它只会在同层级进行, 不会跨层级比较。
如果四种条件都不满足,则利用 vnode.key。先使用 createKeyToOldIdx 生成一个旧节点数组的索引表,如果新节点的 key 不存在这个表中说明是新节点,则添加;如果在则 patchVnode,然后在做一些调整免得影响后面的遍历;
如果 oldStartIdx oldEndIdx 说明旧节点数组先遍历完,这时将剩余的新节点直接新建添加;
否则如果 newStartIdx newEndIdx 说明新节点数组先遍历完,这时将剩余的旧节点直接删除。
模板渲染是 Vue 2.0 与 1.0 最大的不同。本文梳理了模板渲染的过程,重点了其中的 compile 和 patch 函数
网友评论 ()条 查看