Appearance
编译原理
一、编译核心原理
1.通用模板编译器
编译器就是将“源代码”(source code)转化为“目标代码”(target code)的一段代码(code)。整个过程就叫编译(compile)。

由上图可以看到,整个编译过程分为编译前端和编译后端,编译前端包含词法分析、语法分析和语义分析,他通常与目标平台无关,仅负责分析源代码。编译后端则通常与目标平台有关,编译后端包含中间代码生成、优化和目标代码生成。
2.vue 中的模板编译器
整个 vue 的模板编译的过程简单总结,就是如下三步
js
// 1 解析模板:将.vue文件中的template模板转化为AST语法树
const templateAST = parse(template);
// 2 转化代码:将AST语法树转化为JS的语法树
const jsAST = transform(templateAST);
// 3 生成代码:将JS的语法树代码生成为vue需要的渲染函数代码
const code = generate(jsAST);
3.vue 的一个示例
一段 html 源代码
js
<div id="vue">Hello {{ name }}</div>经过 parse 编译之后的结果==>模板 AST
js
{
type: 0,
source: '<div id="vue">Hello {{name}}</div>',
children: [
{
type: 1,
tag: 'div',
ns: 0,
tagType: 0,
props: [
{
type: 6,
name: 'id',
nameLoc: {
start: {
column: 6,
line: 1,
offset: 5,
},
end: {
column: 8,
line: 1,
offset: 7,
},
source: 'id',
},
value: {
type: 2,
content: 'vue',
loc: {
start: {
column: 9,
line: 1,
offset: 8,
},
end: {
column: 14,
line: 1,
offset: 13,
},
source: '"vue"',
},
},
loc: {
start: {
column: 6,
line: 1,
offset: 5,
},
end: {
column: 14,
line: 1,
offset: 13,
},
source: 'id="vue"',
},
},
],
children: [
{
type: 2,
content: 'Hello ',
loc: {
start: {
column: 15,
line: 1,
offset: 14,
},
end: {
column: 21,
line: 1,
offset: 20,
},
source: 'Hello ',
},
},
{
type: 5,
content: {
type: 4,
loc: {
start: {
column: 23,
line: 1,
offset: 22,
},
end: {
column: 27,
line: 1,
offset: 26,
},
source: 'name',
},
content: 'name',
isStatic: false,
constType: 0,
},
loc: {
start: {
column: 21,
line: 1,
offset: 20,
},
end: {
column: 29,
line: 1,
offset: 28,
},
source: '{{name}}',
},
},
],
loc: {
start: {
column: 1,
line: 1,
offset: 0,
},
end: {
column: 35,
line: 1,
offset: 34,
},
source: '<div id="vue">Hello {{name}}</div>',
},
},
],
helpers: {},
components: [],
directives: [],
hoists: [],
imports: [],
cached: 0,
temps: 0,
loc: {
start: {
column: 1,
line: 1,
offset: 0,
},
end: {
column: 35,
line: 1,
offset: 34,
},
source: '<div id="vue">Hello {{name}}</div>',
},
}经过 transform 转化后的结果==>JavaScript AST
js
{
type: 0,
source: '<div id="vue">Hello {{name}}</div>',
children: [
{
type: 1,
tag: 'div',
ns: 0,
tagType: 0,
props: [
{
type: 6,
name: 'id',
nameLoc: {
start: {
column: 6,
line: 1,
offset: 5,
},
end: {
column: 8,
line: 1,
offset: 7,
},
source: 'id',
},
value: {
type: 2,
content: 'vue',
loc: {
start: {
column: 9,
line: 1,
offset: 8,
},
end: {
column: 14,
line: 1,
offset: 13,
},
source: '"vue"',
},
},
loc: {
start: {
column: 6,
line: 1,
offset: 5,
},
end: {
column: 14,
line: 1,
offset: 13,
},
source: 'id="vue"',
},
},
],
children: [
{
type: 8,
loc: {
start: {
column: 15,
line: 1,
offset: 14,
},
end: {
column: 21,
line: 1,
offset: 20,
},
source: 'Hello ',
},
children: [
{
type: 2,
content: 'Hello ',
loc: {
start: {
column: 15,
line: 1,
offset: 14,
},
end: {
column: 21,
line: 1,
offset: 20,
},
source: 'Hello ',
},
},
' + ',
{
type: 5,
content: {
type: 4,
loc: {
start: {
column: 23,
line: 1,
offset: 22,
},
end: {
column: 27,
line: 1,
offset: 26,
},
source: 'name',
},
content: 'name',
isStatic: false,
constType: 0,
},
loc: {
start: {
column: 21,
line: 1,
offset: 20,
},
end: {
column: 29,
line: 1,
offset: 28,
},
source: '{{name}}',
},
},
],
},
],
loc: {
start: {
column: 1,
line: 1,
offset: 0,
},
end: {
column: 35,
line: 1,
offset: 34,
},
source: '<div id="vue">Hello {{name}}</div>',
},
codegenNode: {
type: 13,
tag: '"div"',
props: {
type: 4,
loc: {
start: {
column: 1,
line: 1,
offset: 0,
},
end: {
column: 35,
line: 1,
offset: 34,
},
source: '<div id="vue">Hello {{name}}</div>',
},
content: '_hoisted_1',
isStatic: false,
constType: 2,
hoisted: {
type: 15,
loc: {
start: {
column: 1,
line: 1,
offset: 0,
},
end: {
column: 35,
line: 1,
offset: 34,
},
source: '<div id="vue">Hello {{name}}</div>',
},
properties: [
{
type: 16,
loc: {
start: {
line: 1,
column: 1,
offset: 0,
},
end: {
line: 1,
column: 1,
offset: 0,
},
source: '',
},
key: {
type: 4,
loc: {
start: {
column: 6,
line: 1,
offset: 5,
},
end: {
column: 8,
line: 1,
offset: 7,
},
source: 'id',
},
content: 'id',
isStatic: true,
constType: 3,
},
value: {
type: 4,
loc: {
start: {
column: 9,
line: 1,
offset: 8,
},
end: {
column: 14,
line: 1,
offset: 13,
},
source: '"vue"',
},
content: 'vue',
isStatic: true,
constType: 3,
},
},
],
},
},
children: {
type: 8,
loc: {
start: {
column: 15,
line: 1,
offset: 14,
},
end: {
column: 21,
line: 1,
offset: 20,
},
source: 'Hello ',
},
children: [
{
type: 2,
content: 'Hello ',
loc: {
start: {
column: 15,
line: 1,
offset: 14,
},
end: {
column: 21,
line: 1,
offset: 20,
},
source: 'Hello ',
},
},
' + ',
{
type: 5,
content: {
type: 4,
loc: {
start: {
column: 23,
line: 1,
offset: 22,
},
end: {
column: 27,
line: 1,
offset: 26,
},
source: 'name',
},
content: 'name',
isStatic: false,
constType: 0,
},
loc: {
start: {
column: 21,
line: 1,
offset: 20,
},
end: {
column: 29,
line: 1,
offset: 28,
},
source: '{{name}}',
},
},
],
},
patchFlag: '1 /* TEXT */',
isBlock: true,
disableTracking: false,
isComponent: false,
loc: {
start: {
column: 1,
line: 1,
offset: 0,
},
end: {
column: 35,
line: 1,
offset: 34,
},
source: '<div id="vue">Hello {{name}}</div>',
},
},
},
],
helpers: {},
components: [],
directives: [],
hoists: [
{
type: 15,
loc: {
start: {
column: 1,
line: 1,
offset: 0,
},
end: {
column: 35,
line: 1,
offset: 34,
},
source: '<div id="vue">Hello {{name}}</div>',
},
properties: [
{
type: 16,
loc: {
start: {
line: 1,
column: 1,
offset: 0,
},
end: {
line: 1,
column: 1,
offset: 0,
},
source: '',
},
key: {
type: 4,
loc: {
start: {
column: 6,
line: 1,
offset: 5,
},
end: {
column: 8,
line: 1,
offset: 7,
},
source: 'id',
},
content: 'id',
isStatic: true,
constType: 3,
},
value: {
type: 4,
loc: {
start: {
column: 9,
line: 1,
offset: 8,
},
end: {
column: 14,
line: 1,
offset: 13,
},
source: '"vue"',
},
content: 'vue',
isStatic: true,
constType: 3,
},
},
],
},
],
imports: [],
cached: 0,
temps: 0,
codegenNode: {
type: 13,
tag: '"div"',
props: {
type: 4,
loc: {
start: {
column: 1,
line: 1,
offset: 0,
},
end: {
column: 35,
line: 1,
offset: 34,
},
source: '<div id="vue">Hello {{name}}</div>',
},
content: '_hoisted_1',
isStatic: false,
constType: 2,
hoisted: {
type: 15,
loc: {
start: {
column: 1,
line: 1,
offset: 0,
},
end: {
column: 35,
line: 1,
offset: 34,
},
source: '<div id="vue">Hello {{name}}</div>',
},
properties: [
{
type: 16,
loc: {
start: {
line: 1,
column: 1,
offset: 0,
},
end: {
line: 1,
column: 1,
offset: 0,
},
source: '',
},
key: {
type: 4,
loc: {
start: {
column: 6,
line: 1,
offset: 5,
},
end: {
column: 8,
line: 1,
offset: 7,
},
source: 'id',
},
content: 'id',
isStatic: true,
constType: 3,
},
value: {
type: 4,
loc: {
start: {
column: 9,
line: 1,
offset: 8,
},
end: {
column: 14,
line: 1,
offset: 13,
},
source: '"vue"',
},
content: 'vue',
isStatic: true,
constType: 3,
},
},
],
},
},
children: {
type: 8,
loc: {
start: {
column: 15,
line: 1,
offset: 14,
},
end: {
column: 21,
line: 1,
offset: 20,
},
source: 'Hello ',
},
children: [
{
type: 2,
content: 'Hello ',
loc: {
start: {
column: 15,
line: 1,
offset: 14,
},
end: {
column: 21,
line: 1,
offset: 20,
},
source: 'Hello ',
},
},
' + ',
{
type: 5,
content: {
type: 4,
loc: {
start: {
column: 23,
line: 1,
offset: 22,
},
end: {
column: 27,
line: 1,
offset: 26,
},
source: 'name',
},
content: 'name',
isStatic: false,
constType: 0,
},
loc: {
start: {
column: 21,
line: 1,
offset: 20,
},
end: {
column: 29,
line: 1,
offset: 28,
},
source: '{{name}}',
},
},
],
},
patchFlag: '1 /* TEXT */',
isBlock: true,
disableTracking: false,
isComponent: false,
loc: {
start: {
column: 1,
line: 1,
offset: 0,
},
end: {
column: 35,
line: 1,
offset: 34,
},
source: '<div id="vue">Hello {{name}}</div>',
},
},
loc: {
start: {
column: 1,
line: 1,
offset: 0,
},
end: {
column: 35,
line: 1,
offset: 34,
},
source: '<div id="vue">Hello {{name}}</div>',
},
transformed: true,
}经过 generate 转化后得到一段 code 字符串代码
js
"const _Vue = Vue
const { } = _Vue
const _hoisted_1 = { id: "vue" }
return function render(_ctx, _cache) {
with (_ctx) {
const { toDisplayString: _toDisplayString, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue
return (_openBlock(), _createElementBlock("div", _hoisted_1, "Hello " + _toDisplayString(name), 1 /* TEXT */))
}
}"最后经过 new Function 函数进行编译得到 render 函数
js
import {
toDisplayString as _toDisplayString,
openBlock as _openBlock,
createElementBlock as _createElementBlock,
} from "vue";
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (
_openBlock(),
_createElementBlock(
"div",
{ id: "vue" },
"Hello " + _toDisplayString(_ctx.name),
1 /* TEXT */
)
);
}可以通过进行查询