全部学科
Python全栈
python
NodeJS全栈
nodejs
小程序首页
📅 2026-05-20 10 分钟 ✍️ juanwangdev

虚拟DOM渲染函数生成

Vue通过 codegen 将AST转换为渲染函数字符串,经 Function 构造创建可执行函数,生成虚拟DOM树。

codegen 入口

JavaScript
// src/compiler/codegen/index.js
export function generate(
  ast: ASTElement | void,
  options: CompilerOptions
): CodegenResult {
  const state = new CodegenState(options)
  const code = ast ? genElement(ast, state) : '_c("div")'
  return {
    render: `with(this){return ${code}}`,
    staticRenderFns: state.staticRenderFns
  }
}

AST根节点传入 genElement 生成渲染代码字符串。

genElement 元素生成

JavaScript
function genElement(el, state) {
  // 静态节点优化
  if (el.staticRoot && !el.staticProcessed) {
    return genStatic(el, state)
  }
  // v-once 指令
  else if (el.once && !el.onceProcessed) {
    return genOnce(el, state)
  }
  // v-if 指令
  else if (el.if && !el.ifProcessed) {
    return genIf(el, state)
  }
  // v-for 指令
  else if (el.for && !el.forProcessed) {
    return genFor(el, state)
  }
  // slot 标签
  else if (el.tag === 'slot') {
    return genSlot(el, state)
  }
  // 普通元素
  else {
    let code
    if (el.component) {
      code = genComponent(el.tag, el, state)
    } else {
      const data = el.plain ? undefined : genData(el, state)
      const children = genChildren(el, state)
      code = `_c('${el.tag}'${data ? `,${data}` : ''}${children ? `,${children}` : ''})`
    }
    return code
  }
}

按指令优先级依次处理,普通元素调用 genDatagenChildren 生成属性与子节点代码。

genData 数据属性生成

JavaScript
function genData(el, state) {
  let data = '{'
  
  // key
  if (el.key) {
    data += `key:${el.key},`
  }
  // ref
  if (el.ref) {
    data += `ref:${el.ref},`
  }
  // attrs
  if (el.attrs) {
    data += `attrs:${genProps(el.attrs)},`
  }
  // props
  if (el.props) {
    data += `domProps:${genProps(el.props)},`
  }
  // on
  if (el.events) {
    data += `${genHandlers(el.events, false)},`
  }
  // directives
  if (el.directives) {
    data += `directives:${genDirectives(el.directives)},`
  }
  
  return data + '}'
}

按属性类型分别生成 attrsdomPropsondirectives 等配置。

genChildren 子节点生成

JavaScript
function genChildren(el, state) {
  const children = el.children
  if (children.length === 0) return undefined
  
  // 单节点优化
  if (children.length === 1) {
    return genNode(children[0], state)
  }
  
  // 多节点
  return `[${children.map(c => genNode(c, state)).join(',')}]`
}

function genNode(node, state) {
  if (node.type === 1) return genElement(node, state)
  if (node.type === 3) return node.isComment ? '_e()' : `_v(${text})`
  if (node.type === 2) return `_v(${node.expression})`
}

单节点直接返回,多节点包装为数组。

辅助函数映射

JavaScript
// 渲染函数辅助函数
{
  '_c': createElement,    // 创建元素VNode
  '_v': createTextVNode,  // 创建文本VNode
  '_e': createEmptyVNode, // 创建空注释VNode
  '_u': resolveScopedSlots, // 解析作用域插槽
  '_m': renderStatic,     // 渲染静态节点
  '_l': renderList,       // 渲染v-for列表
  '_s': toString,         // 值转字符串
  '_t': renderSlot        // 渲染插槽
}

编译后的渲染函数使用这些缩写辅助函数创建VNode。

创建可执行函数

JavaScript
// 渲染函数字符串
const render = 'with(this){return _c("div",[_c("h1",[_v("标题")]),_c("p",[_v(_s(msg))])])}'

// 创建可执行函数
const createFunction = (code, errors) => {
  try {
    return new Function(code)
  } catch (e) {
    errors.push({ err: e, code })
    return null
  }
}

const renderFn = createFunction(render, errors)
// 执行时: renderFn.call(vm) 生成VNode树

通过 new Function(code) 将字符串转为可执行函数,执行时通过 call(vm) 绑定实例上下文。

要点总结

  • generate 将AST转为渲染函数字符串,使用 with(this) 简化访问
  • genElement 按指令优先级处理静态、v-if、v-for、slot等
  • genData 生成属性配置对象,genChildren 生成子节点数组
  • 渲染函数使用 _c_v_e 等辅助函数创建VNode
  • 静态节点通过 _m(index) 调用 staticRenderFns
  • 通过 new Function(code) 创建可执行函数,call(vm) 绑定上下文

📝 发现内容有误?点击此处直接编辑

← 上一篇 编译时指令处理
下一篇 → 静态节点标记与优化
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库