Before
编译器说来很简单,它就是一段代码,作用是将源代码翻译成目标代码。
为什么需要翻译呢,因为机器能阅读运行的代码往往晦涩难懂,而人们需要编写易于阅读和维护的代码,
那我们写出来的代码需要运行就需要用到编译器。
Vue.js的模板还有jsx也需要经过编译器才能在浏览器上正常运行。
完整的编译过程有很多步骤,包括词法分析、语法分析、语义分析、中间代码生成、优化、目标代码生成等等。
对于Vue.js模板编译器,它的源代码就是组件的模板,目标代码是能够运行在浏览器上的JavaScript代码。
源代码:
<template>
<div>
<h1 :id="dynamicId">vue template</h1>
</div>
</template>
目标代码:
function render() {
return {
type: 'div',
children: [
{
type: 'h1',
props: {
id: this.dynamicId
},
children: 'vue template'
}
]
}
}
可以看出模板的目标代码就是渲染函数。
整体流程
编译器会先对模板进行词法分析和语法分析,得到模板AST。
接着将模板AST转换成JavaScript AST。
最后根据JavaScript AST生成JavaScript代码也就是渲染函数。
AST即abstract syntax tree,抽象语法树,模板AST也就是用来描述模板的抽象语法树。其结构如下:
const ast = {
type: 'Root',
children: [
{
type: 'Element',
tag: 'div',
children: [
{
type: 'Element',
tag: 'h1',
props: [
{
type: 'Direction',
name: 'if',
exp: {
type: 'Expression',
content: 'ok'
}
}
],
children: 'vue template'
}
]
}
]
}
封装parse函数对模板字符串进行词法和语法分析,得到模板字符串templateAST。
得到模板AST之后,我们就可以对它进行语义分析,也就是进行类型检查、上下文相关分析,比如:
检查v-else指令是否存在对应的v-if指令,分析属性值是否是静态的,是否是常量等等。
完善模板AST之后,封装transform函数将模板AST转化为JavaScript AST。
什么是JavaScript AST呢,因为我们的最终目标是将模板转化为渲染函数,JavaScript AST其实就是设计来描述我们最终要生成的渲染函数。
最后我们封装generate函数,根据JavaScript AST来生成渲染函数。
const template = `
<div>
<h1 v-if="ok">vue template</h1>
</div>
`
const templateAST = parse(template)
const jsAST = transform(templateAST)
const code = generate(jsAST)
接下来我们逐一看这几个步骤的具体实现。
解析器parser
解析器parser的入参是模板字符串,它会逐一读取字符,根据一定的规则将其切个为一个个Token,例如对于如下模板:
<p>vue</p>
解析器会把它切割为三个Token,分别是:开始标签<p>