当你写下:

<template>
  <div class="title">{{ message }}</div>
</template>

Vue 内部究竟发生了什么?

本文将带你深入 Vue 编译器核心,揭秘 Template → AST → Render 函数 的完整链路。


一、核心流程:三步编译流水线

Template (字符串)
     ↓
   parse() → AST (抽象语法树)
     ↓
optimize() → 优化 AST(标记静态节点)
     ↓
generate() → Render 字符串
     ↓
new Function() → Render 函数
     ↓
执行 → VNode → DOM

二、第一步:parse → 生成 AST

目标

将模板字符串转换为 抽象语法树(AST),用 JavaScript 对象描述模板结构。

示例模板

<div class="title" v-if="show">
  Hello {{ name }}
  <span>Static Text</span>
</div>

生成的 AST 结构

{
  type: 1, // 元素节点
  tag: 'div',
  attrsList: [
    { name: 'class', value: 'title' },
    { name: 'v-if', value: 'show' }
  ],
  children: [
    {
      type: 3, // 文本节点
      text: 'Hello ',
      static: false
    },
    {
      type: 2, // 表达式节点
      expression: '_s(name)',
      text: '{{ name }}',
      static: false
    },
    {
      type: 1,
      tag: 'span',
      children: [
        { type: 3, text: 'Static Text', static: true }
      ],
      static: true // 整个 span 是静态的
    }
  ],
  static: false,
  staticRoot: false
}

parse 如何工作?

  1. 正则匹配:顺序扫描模板字符串;
  2. 识别标记
    • <div> → 开始标签 → 执行 start() 回调;
    • </div> → 结束标签 → 执行 end() 回调;
    • Hello {{ name }} → 文本 → 执行 chars() 回调;
  3. 构建树结构:通过栈结构维护父子关系。

三、第二步:optimize → 静态节点优化

目标

标记永不变化的节点,在后续更新中跳过 diff,极大提升性能。

优化策略

节点类型是否静态判断依据
<span>Static</span>纯文本,无绑定
{{ message }}包含变量
<div v-if="false">指令依赖响应式数据
<img src="./logo.png">属性无绑定

优化后 AST 新增标记

{
  tag: 'span',
  static: true,      // 标记为静态节点
  staticRoot: true,  // 标记为静态根(可提升)
  children: [/* ... */]
}

优化带来的性能收益

  • 首次渲染:静态节点生成的 VNode 可被缓存;
  • 更新渲染:直接复用缓存的 VNode,跳过 patch
  • 内存节省:减少不必要的 VNode 创建。

四、第三步:generate → 生成 Render 函数代码

目标

将优化后的 AST 转换为可执行的 render 函数字符串。

生成的 Render 字符串

with(this) {
  return (show) 
    ? _c('div', { staticClass: "title" }, [
        _v("Hello " + _s(name)),
        _c('span', [_v("Static Text")])
      ])
    : _e()
}

关键函数解析

函数作用源码对应
_ccreateElement创建 VNode
_vcreateTextVNode创建文本节点
_stoString转换表达式
_ecreateEmptyVNode创建空节点

generate 如何工作?

  1. 深度优先遍历 AST
  2. 根据节点类型生成对应代码
    • 元素节点 → _c(tag, data, children)
    • 文本节点 → _v(text)
    • 表达式 → _s(expression)
  3. 处理指令
    • v-if → 三元表达式;
    • v-for_l(array, function)
    • v-on → 事件监听对象

五、最终:创建 Render 函数

const render = new Function(`with(this){return ${code}}`)

render 函数执行结果

// 执行 render() 返回 VNode
{
  tag: 'div',
  data: { staticClass: 'title' },
  children: [
    { text: 'Hello Vue', ... },
    { tag: 'span', children: [...], ... }
  ]
}

六、Vue 3 的编译优化

编译时优化

优化说明
静态提升将静态节点提升到 render 外,只创建一次
Patch Flag动态节点打标记,diff 时只比对相关部分
Tree Shaking未使用的转换函数被摇除

Vue 3 Render 函数示例

import { createElementVNode as _createElementVNode } from 'vue'

const _hoisted_1 = /*#__PURE__*/ _createElementVNode("span", null, "Static Text", -1 /* HOISTED */)

return function render(_ctx, _cache) {
  return (_ctx.show)
    ? (_openBlock(), _createElementVNode("div", { class: "title" }, [
        _createTextVNode("Hello " + _toDisplayString(_ctx.name), 1 /* TEXT */),
        _hoisted_1
      ], 64 /* STABLE_FRAGMENT */))
    : _createEmptyVNode()
}

七、开发模式 vs 生产模式

模式编译时机调试支持
开发模式运行时编译(vue.js)模板错误定位精确
生产模式构建时编译(vue.runtime.js)体积小、性能高

结语

阶段输入输出目的
parse模板字符串AST结构化描述
optimizeAST优化 AST标记静态内容
generate优化 ASTRender 字符串生成可执行代码

掌握模板编译过程,你就能:

理解 {{ }} 和指令的底层原理;
优化模板结构提升性能;
调试编译错误更高效;
为学习 Vue 3 编译优化打下基础。

本站提供的所有下载资源均来自互联网,仅提供学习交流使用,版权归原作者所有。如需商业使用,请联系原作者获得授权。 如您发现有涉嫌侵权的内容,请联系我们 邮箱:[email protected]