测试用例
const App = {
template: `<p>{{msg}}123</p>`,
setup() {
return {
msg: "vue3 - compiler",
};
},
};
编译流程
export function baseCompile(template, options) {
// 1. 先把 template 也就是字符串 parse 成 ast
const ast = baseParse(template);
// 2. 生成 codegenNode
transform(
ast,
Object.assign(options, {
nodeTransforms: [transformElement, transformText, transformExpression],
})
);
// 3. 生成 render 函数代码
return generate(ast);
}
1.将 Template 转换为 AST
// const ast = baseParse(template);
{
"type": 1,
"children": [
{
"type": 4,
"tag": "p",
"tagType": 0,
"children": [
{
"type": 2,
"content": {
"type": 3,
"content": "msg"
}
},
{
"type": 0,
"content": "123"
}
]
}
],
"helpers": []
}
2.生成 codegenNode
tips:code generate by node 通过这些节点去生成代码,继续看我们可以发现 p 下面多了一个孩子节点 '+', 因为表达式节点和文本节点在生成真实节点的时候其实就是一个文本节点,所以需要拼接
/**
transform(
ast,
Object.assign(options, {
nodeTransforms: [transformElement, transformText,transformExpression],
})
); */
{
"type": 1,
"children": [
{
"type": 4,
"tag": "p",
"tagType": 0,
"children": [
{
"type": 5,
"children": [
{
"type": 2,
"content": {
"type": 3,
"content": "_ctx.msg"
}
},
" + ",
{
"type": 0,
"content": "123"
}
]
}
],
"codegenNode": {
"type": 4,
"tag": "'p'",
"props": null,
"children": {
"type": 5,
"children": [
{
"type": 2,
"content": {
"type": 3,
"content": "_ctx.msg" // 变化
}
},
" + ", // 变化
{
"type": 0,
"content": "123"
}
]
}
}
}
],
"helpers": [
null,
null
],
"codegenNode": {
"type": 4,
"tag": "'p'",
"props": null,
"children": {
"type": 5,
"children": [
{
"type": 2,
"content": {
"type": 3,
"content": "_ctx.msg"
}
},
" + ",
{
"type": 0,
"content": "123"
}
]
}
}
}
3.生成 code:
tips: 生成的 render 函数可以返回描述这个组件的虚拟节点,后续的执行流程中我们就是拿这个虚拟节点去做 patch 比对,完成真实的渲染。如果将这个过程包裹在一个 effect 里,就可以完成响应式的渲染。
const { toDisplayString: _toDisplayString, createElementVNode: _createElementVNode } = Vue
return function render(_ctx) {
return _createElementVNode('p',
null, _toDisplayString(_ctx.msg) + '123')
}