Vue 源码目录
- 响应式原理
- 模板编译原理
- 异步更新原理
- diff算法原理
- Mixin混入原理
- Vue-router原理
- 生命周期的原理
- ...
本文目录
-
模板编译原理
-
Vue2 的版本
-
相关图解
-
总结
一、模板编译原理
template模板字符串->ast树->优化语法树(标记是否是静态节点)->转换得到新的ast->render函数
AST(抽象语法树)
在开发过程中扮演一个非常重要的角色,但是我们却很少去直接接触它。
无论是代码编译(babel),打包(webpack),代码压缩,css预处理,代码校验(eslint),代码美化(pretiier),Vue中对template的编译,这些的实现都离不开AST。
vue模板编译过程
Vue 提供了 2 个版本,一个是 Runtime + Compiler ,另一个是 Runtime only 的,前者是包含编译代码的,会把编译的过程放在运行时做,后者是不包含编译代码的,需要借助 webpack 的vue-loader把模板编译render函数。不管使用哪个版本,都有一个环节,就是将模板编译成render函数。
我们常用的是运行时版本(不包含编译代码)
Vue 单文件SFC -> webpack (vue-loader) -> 将模板编译成render函数
1) vue模板的编译过程分为3个阶段:
1.1) 解析(Parse)
const ast = parse(template.trim(), options)
将模板字符串解析生成 AST,这里的解析器是vue自己实现的,解析过程中会使用正则表达式对模板顺序解析,当解析到开始标签、闭合标签、文本的时候都会有相对应的回调函数执行,来达到构造 AST 树的目的。
生成的AST 元素节点总共有 3 种类型,1 为普通元素, 2 为表达式,3为纯文本。
例如
<ul :class="bindCls" class="list" v-if="isShow">
<li v-for="(item,index) in data"
@click="clickItem(index)">{{item}}:{{index}}
</li>
</ul>
上面模板解析生成的AST树如下:
ast = {
'type': 1,
'tag': 'ul',
'attrsList': [],
'attrsMap': {
':class': 'bindCls',
'class': 'list',
'v-if': 'isShow'
},
'if': 'isShow'
......
}
1.2) 优化语法树(Optimize)【是否是静态节点做标记】
vue模板中并不是所有数据都是响应式的,有很多数据是首次渲染后就永远不会变化的,那么这部分数据生成的 DOM 也不会变化,我们可以在patch的过程跳过对他们的比对.
此阶段会深度遍历生成的 AST树,检测它的每一颗子树是不是静态节点,如果是静态节点则它们生成 DOM 永远不需要改变,这对运行时对模板的更新起到极大的优化作用.确保静态的数据不会进入虚拟 DOM 的更新阶段,以此来优化性能。
遍历过程中,会对整个 AST 树中的每一个 AST 元素节点标记static和staticRoot(递归该节点的所有children,一旦子节点有不是static的情况,则为false,否则为true).
经过该阶段,上面例子中的ast会变成:
ast = {
'type': 1,
'static': false,// 每个节点都会新增此属性
'tag': 'ul',
'attrsList': [],
'attrsMap': {
':class': 'bindCls',
'class': 'list',
'v-if': 'isShow'
},
'if': 'isShow'
......
}
1.3) 生成代码,将ast生成render函数
const code = generate(ast, options)
通过generate方法,将ast生成render函数:
with(this){
return (isShow) ?
_c('ul', {
staticClass: "list",
class: bindCls
},
_l((data), function(item, index) {
return _c('li', {
on: {
"click": function($event) {
clickItem(index)
}
}
},
[_v(_s(item) + ":" + _s(index))])
})
) : _e()
}
看到这里一堆的下划线肯定很懵逼,这里的 _c 对应的是虚拟 DOM 中的 createElement 方法。其他的下划线方法在 core/instance/render-helpers 中都有定义,每个方法具体做了什么不做展开。
二、Vue2 的版本
很多人使用 Vue 的时候,都是直接通过 vue-cli 生成的模板代码,并不知道 Vue 其实提供了两个构建版本。
vue.js: 完整版本,包含了模板编译的能力;vue.runtime.js: 运行时版本,不提供模板编译能力,需要通过 vue-loader 进行提前编译。
简单来说,就是如果你用了 vue-loader ,就可以使用 vue.runtime.min.js,将模板编译的过程交过 vue-loader,如果你是在浏览器中直接通过 script 标签引入 Vue,需要使用 vue.min.js,运行的时候编译模板。
三、参考
四、相关图解
五、总结
-
我们常用的是Vue的运行时版本 Vue 单文件SFC -> webpack (vue-loader) -> 将模板编译成render函数
-
template模板字符串->ast树->优化语法树(标记是否是静态节点)->转换得到新的ast->render函数 -
【template str -> ast -> 优化ast -> render -> VNode -> patch与之前的VNode -> 得出差异后将这些差异渲染到真实的DOM】