Skip to content

创建生成上下文

生成代码时我们需要将生成的代码拼接成字符串,同时添加换行缩进等。

function createCodegeContext(){
    const context = {
        code:``,
        indentLevel:0,
        helper(key){ return `_${helperNameMap[key]}`},
        push(code){context.code += code;},
        indent(){ // 前进
            newline(++context.indentLevel)
        },
        deindent(withoutnewline = false){ // 缩进
            if (withoutnewline) {
                --context.indentLevel
            } else {
                newline(--context.indentLevel)
            }
        },
        newline(){ newline(context.indentLevel) } // 换行
    }
    function newline(n){ context.push('\n'+`  `.repeat(n))}
    return context
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function generate(ast){
    const context = createCodegeContext();
}
1
2
3
export function compile(template){
    // 1.将模板转化成ast语法树
    const ast = baseParse(template);
    // 2.对ast语法树进行转化
    transform(ast);
    // // 3.生成代码
    return generate(ast)
}
1
2
3
4
5
6
7
8

生成文本代码

 let r = compile('hello, jw')
1

如果仅仅是文本直接返回即可~

function genText(node,context){
    context.push(JSON.stringify(node.content)) // 添加文本代码
}
function genFunctionPreamble(ast,context){ // 生成函数
    const {push,newline} = context
    if(ast.helpers.length > 0){ // 生成导入语句
       push(`const {${ast.helpers.map((s)=>`${helperNameMap[s]}:_${helperNameMap[s]}`).join(', ')}} = Vue`)
    }
    newline()
    push(`return `)
}
function genNode(node,context){
    switch(node.type){
        case NodeTypes.TEXT:
            genText(node,context) 
            break;
    }
}
function generate(ast){
    const context = createCodegeContext();
    const {push,indent} = context
    genFunctionPreamble(ast,context);
    const functionName = 'render';
    const args = ['_ctx','$props'];
    push(`function ${functionName}(${args.join(', ')}){`)
    indent();
    push(`return `)
    if (ast.codegenNode) {
        genNode(ast.codegenNode, context)
    } else {
        push(`null`)
    }
    return context.code
}
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
31
32
33
34

生成表达式代码

let r = compile('{{age}}')
1
function genExpression(node,context){
    const { content } = node
    context.push(content)
}
function genInterpolation(node,context){
    const { push, helper } = context
    push(`${helper(TO_DISPLAY_STRING)}(`)
    genNode(node.content, context)
    push(`)`)
}
function genNode(node,context){
    switch(node.type){
        case NodeTypes.TEXT:
            genText(node,context)
            break;
        case NodeTypes.INTERPOLATION: // 生成表达式
            genInterpolation(node,context)
            break; 
        case NodeTypes.SIMPLE_EXPRESSION: // 简单表达式的处理
            genExpression(node, context)
            break
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

生成元素表达式

let r = compile(`<div a='1' b='2'>123</div>`)
1
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(Array.isArray(node)){
            genNodeList(node, context)
        } else {
            genNode(node, context);
        }
        if (i < nodes.length - 1) {
            push(", ");
        }
    }
}
function genVNodeCall(node,context){
    const {push,helper} = context;
    const {tag,props,children,isBlock} = node

    if(isBlock){
        push(`(${helper(OPEN_BLOCK)}(),`)
    }
    // 生成createElementBlock或者createElementVnode
    const callHelper = isBlock ? CREATE_ELEMENT_BLOCK: CREATE_ELEMENT_VNODE; 
    push(helper(callHelper));
    push('(');
    genNodeList([tag, props, children].map(item=>item || 'null'), context);
    push(`)`)
    if(isBlock){
        push(`)`)
    }
}
function genNode(node,context){
    switch(node.type){
        case NodeTypes.VNODE_CALL: // 元素调用
            genVNodeCall(node,context);
            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
31
32
33
34
35
36
37
38
39
40

生成元素属性

function genObjectExpression(node, context) {
    const { push, newline } = context
    const { properties } = node
    if (!properties.length) {
        push(`{}`)
        return
      }
    push('{')
    for (let i = 0; i < properties.length; i++) {
        const { key, value } = properties[i]
        // key
        push(key);
        push(`: `)
        push(JSON.stringify(value));
        // value
        if (i < properties.length - 1) {
          push(`,`)
        }
    }
    push('}')
}
function genNode(node,context){
    switch(node.type){
        case NodeTypes.JS_OBJECT_EXPRESSION: // 元素属性
            genObjectExpression(node, context)
            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

处理Fragment情况

如果是Fragment,则将字符串和symbol直接放入代码中,对元素再次进行递归处理

let r = compile(`<div>123</div><div>123</div>`)
1
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.ELEMENT:
            genNode(node.codegenNode,context)

    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

处理复合表达式

let r = compile(`<div>123 {{abc}}</div>`)
1
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)
        }
    }
}
function genNode(node,context){
    switch(node.type){
        // ...
        case NodeTypes.COMPOUND_EXPRESSION:
            genCompoundExpression(node, context)
            break
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

复合表达式+元素处理

let r = compile(`<div>123 {{name}} <span>{{name}}</span></div>`)
1
function genCallExpression(node, context) {
    const { push, helper } = context
    const callee = helper(node.callee)

    push(callee + `(`, node)
    genNodeList(node.arguments, context)
    push(`)`)
}
function genNode(node,context){
    switch(node.type){
        // ...
        case NodeTypes.TEXT_CALL: // 对文本处理
            genNode(node.codegenNode, context)
            break
        case NodeTypes.JS_CALL_EXPRESSION: // 表达式处理
            genCallExpression(node, context)
            break
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

Released under the MIT License.