Vue3 模版编译流程

147 阅读1分钟

测试用例

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')
}

学习资料

Vue Template Explorer

mini-vue

vuejs/core