从零手写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

用于做生成代码时产生的方法名

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.生成函数添加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

生成导入语句

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

二.转化方式

1.对block节点转化

case NodeTypes.VNODE_CALL:
	// 1.最外层元素
    genVNodeCall(node, context)
1
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
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
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.对元素节点转化

case NodeTypes.ElEMENT:
    genNode(node.codegenNode, context)
1
2

3.对文本节点转化

case NodeTypes.TEXT:
    genText(node, context);
1
2
function genText(node,context) {
    context.push(JSON.stringify(node.content), node)
}
1
2
3

4.对表达式的转化

case NodeTypes.INTERPOLATION:
	genInterpolation(node, context)
1
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

5.简单表达式

case NodeTypes.SIMPLE_EXPRESSION:
    genExpression(node, context)
1
2
function genExpression(node, context) {
    const { content, isStatic } = node
    context.push(content)
}
1
2
3
4

6.复合表达式

case NodeTypes.COMPOUND_EXPRESSION:
    genCompoundExpression(node, context)
1
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

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
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