codegen 生成 element

在本小节中,我们需要将一个 template 生成为 render

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

1. 测试样例

我们先写一个测试样例,这一次,我们可以采用快照的形式来看自己生成的 code string

test('simple element', () => {
  const template = '<div></div>'
  const ast = baseParse(template)
  const code = codegen(ast)
  expect(code).toMatchSnapshot()
})

2. 实现

首先,我们在 runtimeHelpers 中可以加入这个类型

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 的 transform 插件,记得在 transform 模块中执行每个插件的时候再传递一个 context 参数。

import { NodeType } from '../ast'
import { CREATE_ELEMENT_VNODE } from '../runtimeHelpers'

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

然后在测试的时候将这个插件加入其中。

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

最后在 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
  }
}

// 处理 element
function genElement(node, context) {
  const { push, helper } = context
  const { tag } = node
  push(`${helper(CREATE_ELEMENT_VNODE)}('${tag}')`)
}