vue的模版编译过程主要如下:template -> ast -> render函数 -> 虚拟DOM -> 真实DOM
*
读取模板:Vue 会读取 HTML 模板并将其转换为字符串。
*
解析模板:Vue 使用编译器将字符串模板转换为抽象语法树(AST),其中包含模板中的每个元素和它们的属性。
*
生成 render 函数:Vue 使用抽象语法树生成 render 函数。
*
数据响应:Vue 将数据绑定到 render 函数,并使用 Object.defineProperty 监听数据的变化,在数据更改时重新生成 render
函数。
*
虚拟 DOM:Vue 通过 render 函数生成虚拟 DOM,该数据结构是真实 DOM 的内存版本。
*
更新 DOM:Vue 通过对比虚拟 DOM 和真实 DOM 的差异,仅更新需要更新的部分,从而生成最终的真实 DOM。
vue 在模版编译版本的码中会执行 compileToFunctions 将template转化为render函数:
// 将模板编译为render函数 const { render, staticRenderFns } = compileToFunctions({
template,options //省略 }, this) 复制代码
CompileToFunctions中的主要逻辑如下∶
(1) 调用parse方法将template转化为ast(抽象语法树)
const ast = parse(template.trim(), options) 复制代码
*
parse的目标:把tamplate转换为AST树,它是一种用 JavaScript对象的形式来描述整个模板。
*
解析过程:利用正则表达式顺序解析模板,当解析到开始标签、闭合标签、文本的时候都会分别执行对应的 回调函数,来达到构造AST树的目的。
AST元素节点总共三种类型:type为1表示普通元素、2为表达式、3为纯文本
(2) 对静态节点做优化
optimize(ast,options) 复制代码
这个过程主要分析出哪些是静态节点,给其打一个标记,为后续更新渲染可以直接跳过静态节点做优化
深度遍历AST,查看每个子树的节点元素是否为静态节点或者静态节点根。如果为静态节点,他们生成的DOM永远不会改变,这对运行时模板更新起到了极大的优化作用。
(3) 生成render函数
const code = generate(ast, options) 复制代码
*
生成 render 函数的字符串形式
在这一步中,会遍历整个 AST,根据节点类型和属性值,生成一个字符串形式的 render 函数,其中包括 VNode 节点的创建、属性的设置等操作。
*
将 render 函数字符串转化为函数
将生成的字符串形式的 render 函数,通过 new Function() 的方式转化为真正的函数。在这个过程中,会对 render
函数进行一些优化,比如使用 with 语句将 this 指向 vm 实例,从而提高执行效率。
*
执行 render 函数生成 VNode
将生成的 render 函数执行,得到一个 VNode 节点,用于渲染视图。
generate将ast抽象语法树编译成 render字符串并将静态部分放到 staticRenderFns 中,最后通过 new Function(``
render``) 生成render函数。
(4) 生成虚拟Dom
render函数的执行过程如下:
*
render函数被调用,传入createElement函数作为参数。
*
在render函数中,调用createElement函数来创建虚拟DOM节点。
*
在createElement函数中,根据传入的标签名、属性和子节点,创建一个虚拟DOM节点。
*
如果子节点是一个字符串,将其转化为文本节点。
*
如果子节点是一个数组,遍历数组中的每个元素,并递归调用createElement函数来创建子节点。
*
最终返回一个包含所有子节点的虚拟DOM节点。
// 模板字符 <div> {{message}} </div> // AST Dom树 { type: "Program", children: [ {
type: "Tag", tagName: "div", children: [ { type: "Expression", expression:
"message" } ] } ] } // 生成的 render 函数为 function render () { return h('div',
[this.message]); } // 虚拟dom { tag: 'div', children: [ { type: 2, expression:
'message', text: undefined } ] } tag: 节点类型,此处为 'div' children: 子节点数组,包含了一个文本节点
type: 节点类型,2 代表文本节点 expression: 插值表达式 'message' text: 未被渲染的文本,因为在渲染的时候需要通过
expression 来获取相应的值,所以为 undefined。 复制代码