通过查看vue源码,可以知道Vue源码中使用了虚拟DOM(Virtual Dom),虚拟DOM构建经历 template编译成AST语法树 -> 再转换为render函数 最终返回一个VNode(VNode就是Vue的虚拟DOM节点) 。
本文通过对Vue源码中的AST转化部分进行简单提取,返回静态的AST结构(不考虑兼容性及属性的具体解析)。并最终根据一个实例的template转化为最终的AST结构。
什么是AST
在Vue的mount过程中,template会被编译成AST语法树,AST是指抽象语法树(abstract syntax tree或者缩写为AST),或者语法树(syntax tree),是源代码的抽象语法结构的树状表现形式。
代码分析
首先、定义一个简单的html DOM结构、其中包括比较常见的标签、文本以及注释,用来生成AST结构。
很粗
很简单,我就是一程序员
姓名:{ {name}},年龄:{ {age}},
请联系我吧
var vm = new Vue({
el: '#app',
// template: '#template',
// template: 'string template',
// template: document.querySelector('#template'),
data () {
return {
name: 'Jeffery',
age: '26'
}
},
comments: true, // 是否保留注释
// delimiters: ['{', '}'] // 定义分隔符,默认为"{ {}}"
})
对于转成AST,则需要先获取template,对于这部分内容,做一个简单的分析,具体的请自行查看Vue源码。
具体目录请参考: '/src/platforms/web/entry-runtime-with-compiler'
从vue官网中知道,vue提供了两个版本,完整版和只包含运行时版,差别是完整版包含编译器,就是将template模板编译成AST,再转化为render函数的过程,因此只包含运行时版必须提供render函数。
注意:此处处理比较简单,只是为了获取template,以便用于生成AST。
function Vue (options) {
// 如果没有提供render函数,则处理template,否则直接使用render函数
if (!options.render) {
let template = options.template;
// 如果提供了template模板
if (template) {
// template: '#template',
// template: '
if (typeof template === 'string') {
// 如果为'#template'
if (template.charAt(0) === '#') {
let tpl = query(template);
template = tpl ? tpl.innerHTML : '';
}
// 否则不做处理,如:'
} else if (template.nodeType) {
// 如果模板为DOM节点,如:template: document.querySelector('#template')
// 比如:
template = template.innerHTML;
}
} else if (options.el) {
// 如果没有模板,则使用el
template = getOuterHTML(query(options.el));
}
if (template) {
// 将template模板编译成AST(此处省略一系列函数、参数处理过程,具体见下图及源码)
let ast = null;
ast = parse(template, options);
console.log(ast)
}
}
}
可以看出:在options中,vue默认先使用render函数,如果没有提供render函数,则会使用template模板,最后再使用el,通过解析模板编译AST,最终转化为render。
其中函数如下:
function query (el) {
if (typeof el === 'string') {
var selected = document.querySelector(el);
if (!selected) {
console.error('Cannot find element: ' + el);
}
return selected;
}
return el;
}
function getOuterHTML (el) {
if (el.outerHTML) {
return el.outerHTML;
} else {
var dom = document.createElement('div');
dom.appendChild(el.cloneNode(true));
return dom.innerHTML;
}
}
对于定义组件模板形式,可以参考下这篇文章
说了这么多,也不废话了,下面重点介绍template编译成AST的过程。
根据源码,先定义一些基本工具方法,以及对相关html标签进行分类处理等。
// script、style、textarea标签
function isPlainTextElement (tag) {
let tags = {
script: true,
style: true,
textarea: true
}
return tags[tag]
}
// script、style标签
function isForbiddenTag (tag) {
let tags = {
script: true,
style: true
}
return tags[tag]
}
// 自闭和标签
function isUnaryTag (tag) {