【vue篇】Vue 模板编译全解析:从 Template 到 DOM 的奇妙旅程

当你写下:

vue 复制代码
<template>
  <div class="title">{{ message }}</div>
</template>

Vue 内部究竟发生了什么?

"模板是如何变成可执行的 JavaScript 函数的?" "为什么 {{ message }} 能自动更新?" "静态内容是如何被优化的?"

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


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

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

二、第一步:parse → 生成 AST

✅ 目标

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

📌 示例模板

html 复制代码
<div class="title" v-if="show">
  Hello {{ name }}
  <span>Static Text</span>
</div>

🌲 生成的 AST 结构

js 复制代码
{
  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. 构建树结构:通过栈结构维护父子关系。

💡 类似 HTML 解析器,但输出是 AST 而非 DOM。


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

✅ 目标

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

🎯 优化策略

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

📊 优化后 AST 新增标记

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

⚡ 优化带来的性能收益

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

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

✅ 目标

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

🧩 生成的 Render 字符串

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

🔍 关键函数解析

函数 作用 源码对应
_c createElement 创建 VNode
_v createTextVNode 创建文本节点
_s toString 转换表达式
_e createEmptyVNode 创建空节点

🏗️ generate 如何工作?

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

五、最终:创建 Render 函数

js 复制代码
const render = new Function(`with(this){return ${code}}`)

render 函数执行结果

js 复制代码
// 执行 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 函数示例

js 复制代码
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()
}

💥 -1 表示该节点永不更新1 表示只 diff 文本内容


七、开发模式 vs 生产模式

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

✅ 推荐:使用 vue-cli / Vite 在构建时编译模板


💡 结语

"Vue 模板不是魔法,而是编译的艺术。"

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

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

✅ 理解 {{ }} 和指令的底层原理;

✅ 优化模板结构提升性能;

✅ 调试编译错误更高效;

✅ 为学习 Vue 3 编译优化打下基础。

相关推荐
LuckySusu3 小时前
【vue篇】Vue 响应式更新揭秘:数据变了,DOM 为何不立即刷新?
前端·vue.js
LuckySusu3 小时前
【vue篇】Vue 事件修饰符完全指南:精准控制事件流
前端·vue.js
6269603 小时前
前端页面出现问题ResizeObserver loop completed with undelivered notifications.
前端
LuckySusu3 小时前
【vue篇】Vue 组件继承与混入:mixin 与 extends 的合并逻辑深度解析
前端·vue.js
LuckySusu3 小时前
【vue篇】Vue 中保持页面状态的终极方案:从卸载到缓存
前端·vue.js
IT_陈寒4 小时前
Python 3.11性能翻倍秘诀:7个你从未注意过的隐藏优化点!
前端·人工智能·后端
学习编程的Kitty5 小时前
算法——位运算
java·前端·算法
程序猿阿伟5 小时前
《3D动作游戏受击反馈:从模板化硬直到沉浸式打击感的开发拆解》
前端·网络·3d
jsonchao5 小时前
web 菜鸟级选手,纯好玩,做了 1 个小游戏网站(我感觉挺好玩😂)
前端