Vue3
中编译原理(三)
从零手写一.Vue3 codegen实现
export const helperNameMap: any = {
[FRAGMENT]: `Fragment`,
[OPEN_BLOCK]: `openBlock`,
[CREATE_BLOCK]: `createBlock`,
[CREATE_VNODE]: `createVNode`,
[TO_DISPALY_STRING]: "toDisplayString",
[CREATE_TEXT]: "createTextVNode"
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
用于做生成代码时产生的方法名
1.生成上下文
function createCodegenContext(ast) {
function newline(n) {
context.push('\n' + ` `.repeat(n))
}
const context = {
code: ``, // 拼接出的代码
indentLevel: 0, // 缩进
helper(key){
return `${helperNameMap[key]}`;
},
push(code) {
context.code += code;
},
newline() { // 新行
newline(context.indentLevel)
},
indent() { // 新行 + 2空格
newline(++context.indentLevel);
},
deindent() {// 缩进 2空格
newline(--context.indentLevel);
}
}
return context;
}
function generate(ast) {
const context = createCodegenContext(ast); // 生成代码时所需要的上下文
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2.生成函数添加with
function generate(ast) {
const context = createCodegenContext(ast); // 生成代码时所需要的上下文
const { indent, deindent, push, newline } = context;
push(`function render(_ctx){`);
indent();
push(`with(_ctx){`);
indent();
push('// todo import..');
newline()
push(`return `);
push(`// todo vnode`)
deindent();
push(`}`);
deindent();
push('}');
return context.code
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
生成导入语句
push(`const {${ast.helpers.map(s => `${helperNameMap[s]}`).join(',')}} = _Vue`);
1
生成return语句
genNode(ast.codegenNode, context)
1
3.对不同类型做转化
function genNode(node, context) {
if (isString(node)) {
context.push(node)
return
}
if (isSymbol(node)) {
context.push(context.helper(node))
return
}
switch (node.type) {
case NodeTypes.VNODE_CALL:
break;
case NodeTypes.ElEMENT:
break;
case NodeTypes.TEXT:
break;
case NodeTypes.INTERPOLATION:
break;
case NodeTypes.COMPOUND_EXPRESSION:
break;
case NodeTypes.SIMPLE_EXPRESSION:
break;
case NodeTypes.TEXT_CALL:
break
case NodeTypes.JS_CALL_EXPRESSION:
break
default:
break;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
二.转化方式
1.对block节点转化
case NodeTypes.VNODE_CALL:
// 1.最外层元素
genVNodeCall(node, context)
1
2
3
2
3
function genVNodeCall(node, context) {
const { push, helper } = context
const { tag, props, children, patchFlag, isBlock } = node;
if (isBlock) {
push(`(${helper(OPEN_BLOCK)}(),`)
}
push(helper(isBlock ? CREATE_BLOCK : CREATE_VNODE) + '(', node)
genNodeList([tag, props, children, patchFlag], context)
push(`)`);
if (isBlock) {
push(`)`)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
function genNodeList(nodes, context) {
const { push } = context
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
if (isString(node)) {
push(node);
} else if (node == null) {
push(`null`);
} else if (isArray(node)) { // 是数组就循环
genNodeListAsArray(node, context)
} else { // 否则genNode
genNode(node, context)
}
if (i < nodes.length - 1) {
push(',')
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function genNodeListAsArray(nodes, context) {
context.push(`[`)
context.indent();
console.log(nodes)
genNodeList(nodes, context);
context.deindent();
context.push(']')
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
2.对元素节点转化
case NodeTypes.ElEMENT:
genNode(node.codegenNode, context)
1
2
2
3.对文本节点转化
case NodeTypes.TEXT:
genText(node, context);
1
2
2
function genText(node,context) {
context.push(JSON.stringify(node.content), node)
}
1
2
3
2
3
4.对表达式的转化
case NodeTypes.INTERPOLATION:
genInterpolation(node, context)
1
2
2
function genInterpolation(node, context) {
const { push, helper } = context
push(`${helper(TO_DISPALY_STRING)}(`)
genNode(node.content, context)
push(`)`)
}
1
2
3
4
5
6
2
3
4
5
6
5.简单表达式
case NodeTypes.SIMPLE_EXPRESSION:
genExpression(node, context)
1
2
2
function genExpression(node, context) {
const { content, isStatic } = node
context.push(content)
}
1
2
3
4
2
3
4
6.复合表达式
case NodeTypes.COMPOUND_EXPRESSION:
genCompoundExpression(node, context)
1
2
2
function genCompoundExpression(
node,
context
) {
for (let i = 0; i < node.children!.length; i++) {
const child = node.children![i]
if (isString(child)) {
context.push(child)
} else {
genNode(child, context)
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
7.文本函数处理
case NodeTypes.TEXT_CALL:
genNode(node.codegenNode, context)
break
case NodeTypes.JS_CALL_EXPRESSION:
genCallExpression(node, context)
break
1
2
3
4
5
6
2
3
4
5
6
function genCallExpression(node, context) {
const { push, helper } = context
const callee = isString(node.callee) ? node.callee : helper(node.callee)
push(callee + `(`, node)
genNodeList(node.arguments, context)
push(`)`)
}
1
2
3
4
5
6
7
2
3
4
5
6
7