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。复制代码