模版编译
联系前文,讲了虚拟DOM的patch过程,而虚拟DOM的前提是先有VNode,那么VNode又是从哪里来的?接下来讲的模版编译便是:把用户写的模版进行编译,就会产生VNode。
在日常开发中,我们把写在<template></template>
标签中的类似于原生HTML的内容称之为模版。之所以说是类似于而不是原生html内容是因为在这个标签中不仅写了一些HTML原生内容,还写一些VUE独有的东西,比如v-if,v-for指令,slot插槽等,这些是原生中所没有的,不能被接受的,但实际上我们确实这样写了,也正确显示了,这就是因为Vue有模版编译功能,它会将template里面的所有内容进行编译,把原生HTML找出来解析,再把非原生内容找出经过一系列处理生成渲染函数,也就是render函数,而render函数会将模版生成对应的VNode,而VNode再经过之前介绍的patch过程从而得到将要渲染的视图的VNode,创建真实DOM节点,完成视图更新。
1.渲染流程
2.模版编译流程
2.1.抽象语法树AST
用户在template中写的模版对于Vue来说就是一堆字符串,那么如何解析这一堆字符串并且从中提取出元素的各个属性呢?这就需要一个叫做抽象语法树的东西。
Abstract Syntax Tree , AST抽象语法树,是源代码语法结构的一种抽象表示。它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。之所以说是抽象的,是因为这里的语法并不会表示出真实语法中出现的每个细节。
比如,嵌套括号被隐含在树的结构中,并没有以节点的形式呈现;而类似于if-condition-then这样的条件跳转语句,可以使用带有两个分支的节点来表示。——来自百度百科
这里推荐一个网站
https://astexplorer.net/
这里可以转换html标签成为一个JS对象
2.2 具体流程
将一堆字符串模版解析成抽象语法树AST后,我们就可以对其进行各种操作了,处理完后用处理后的AST来生成render函数,其具体流程可以大致分为三个阶段:
1.模版解析阶段:用正则表达式等方法解析为AST
2.优化阶段:遍历AST,将静态节点标记
3.代码生成阶段:将AST转换为渲染函数
这三个阶段在源码中对应三个模块:
- 模板解析阶段——解析器——源码路径:
src/compiler/parser/index.js
; - 优化阶段——优化器——源码路径:
src/compiler/optimizer.js
; - 代码生成阶段——代码生成器——源码路径:
src/compiler/codegen/index.js
;
对应源码如下:
// 源码位置: /src/complier/index.js
export const createCompiler = createCompilerCreator(function baseCompile (
template: string,
options: CompilerOptions
): CompiledResult {
// 模板解析阶段:用正则等方式解析 template 模板中的指令、class、style等数据,形成AST
const ast = parse(template.trim(), options)
if (options.optimize !== false) {
// 优化阶段:遍历AST,找出其中的静态节点,并打上标记;
optimize(ast, options)
}
// 代码生成阶段:将AST转换成渲染函数;
const code = generate(ast, options)
return {
ast,
render: code.render,
staticRenderFns: code.staticRenderFns
}
})
- const ast =parse(template.trim(), options):
parse
会用正则等方式解析template
模板中的指令、class
、style
等数据,形成AST
。 - optimize(ast, options):
optimize
的主要作用是标记静态节点,这是Vue
在编译过程中的一处优化,挡在进行patch
的过程中,DOM-Diff
算法会直接跳过静态节点,从而减少了比较的过程,优化了patch
的性能。 - const code =generate(ast, options): 将
AST
转化成render
函数字符串的过程,得到结果是render
函数 的字符串以及staticRenderFns
字符串。
最终baseCompile的返回值:
{
ast: ast,
render: code.render,
staticRenderFns: code.staticRenderFns
}
code是一个对象