39- codegen 生成 element

153 阅读1分钟

实现例子

element 类型 的 codegen 跟官网的不一样,官网的是优化版的,我们这只实现核心功能。

<div></div>
// 生成为
const { createElementVNode: _createElementVNode } = Vue
export function render(_ctx, _cache) { return _createElementVNode(
'div') }

测试样例

import { transformElement } from "../src/transforms/transformElement";

test("simple element", () => {
  const template = "<div></div>";
  const ast = baseParse(template);
  transform(ast, {
    // 加入处理插件
    nodeTransforms: [transformElement],
  });
  const code = codegen(ast);
  expect(code).toMatchSnapshot();
});

实现

新增 transformElement plugin

transformElement 里面主要是帮我们把 element 对应的 hepler 添加进去

/*
 * @Author: Lin ZeFan
 * @Date: 2022-04-10 10:45:42
 * @LastEditTime: 2022-04-17 13:16:34
 * @LastEditors: Lin ZeFan
 * @Description:
 * @FilePath: \mini-vue3\src\compiler-core\src\transforms\transformElement.ts
 *
 */
import { NodeType } from "../ast";
import { CREATE_ELEMENT_VNODE } from "../runtimeHelpers";

export function transformElement(node, context) {
  if (node.type === NodeType.ELEMENT) {
    context.helper(CREATE_ELEMENT_VNODE);
  }
}

这里引用了 context.hepler,我们前面调用 plugin 的时候没有把对应的context传过来,所以要优化一下

function traverseNode(node, context) {
  const { nodeTransforms } = context;

  for (let index = 0; index < nodeTransforms.length; index++) {
    const transform = nodeTransforms[index];
+    transform(node, context);
  }

  // 在这里遍历整棵树的时候,将根据不同的 node 的类型存入不同的 helper
  switch (node.type) {
    case NodeType.INTERPOLATION:
      context.helper(TO_DISPLAY_STRING);
      break;
    case NodeType.ROOT:
    case NodeType.ELEMENT:
      // 只有在 ROOT 和 ELEMENT 才会存在 children
      traverseChildren(node, context);
      break;
    default:
      break;
  }
}

新增 element helper

export const TO_DISPLAY_STRING = Symbol('toDisplayString')
// 加入 createElementVNode
export const CREATE_ELEMENT_VNODE = Symbol('createElementVNode')

export const HelperNameMapping = {
  [TO_DISPLAY_STRING]: 'toDisplayString',
  [CREATE_ELEMENT_VNODE]: 'createElementVNode',
}

判断 element 类型

最后在 codegen 阶段加入对于 element 的处理。

function genNode(node, context) {
  switch (node.type) {
    case NodeType.TEXT:
      genText(node, context)
      break
    case NodeType.INTERPOLATION:
      genInterpolation(node, context)
      break
    case NodeType.SIMPLE_EXPRESSION:
      genExpression(node, context)
      break
    // 加入对 element 的处理
    case NodeType.ELEMENT:
      genElement(node, context)
      break
  }
}

function genElement(node, context) {
  const { push } = context;
  const { tag } = node;
  push(`${helper(CREATE_ELEMENT_VNODE)}('${tag}')`);
}