Vue模板渲染核心是模板→AST→渲染函数→VNode的四阶段流水线:先解析模板为静态AST树,再编译为可执行render函数,执行后生成含type/props/children等字段的VNode对象树。

Vue.js 的模板渲染过程,核心是将开发者编写的 <template></template> 字符串(或 DOM 模板)逐步解析、编译为可执行的 JavaScript 对象——即 VNode(虚拟节点),最终由 patch 算法驱动真实 DOM 更新。这个转化不是一步到位,而是分阶段、有明确职责的流水线。
模板字符串 → AST 抽象语法树
Vue 在编译阶段(运行时编译或构建时预编译)首先对模板进行词法分析和语法分析,生成一棵结构化的 AST 节点树。每个 AST 节点描述了模板中一个语法单元:比如元素节点(div)、文本节点({{ msg }})、指令(v-if、v-for)、属性(:id)、插槽等。
关键点:
- AST 不含运行时逻辑,纯静态结构,便于后续优化(如静态提升、hoist 静态节点)
- 模板中的 Mustache 表达式(
{{ }})会被解析为expression字段,保留原始字符串以便后续生成渲染函数时调用with(this){...}或作用域插值 - 指令和绑定属性被提取并挂载到对应 AST 节点的
directives或attrsList/attrsMap中
AST → 渲染函数(Render Function)
接下来,Vue 的编译器遍历 AST,递归地将其转换为可执行的 JavaScript 函数体代码(字符串),再通过 new Function() 动态生成渲染函数(render)。这个函数在组件实例化后被调用,返回 VNode。
立即学习“前端免费学习笔记(深入)”;
例如:<div id="app" :class="cls">{{ text }}</div> 会生成类似这样的渲染函数片段:
其中 h 是 createElement 的别名(Vue 3 中为 h(),Vue 2 中常为 vm.$createElement)。
注意:
- 模板编译只发生在首次(或热更新时),生成的
render函数会被缓存,避免重复解析 - 使用单文件组件(.vue)时,
<template></template>默认由@vue/compiler-sfc编译;若手写render函数,则跳过此步 - Vue 3 的编译器更激进地做静态分析,比如自动将纯静态节点标记为
isStatic,并在生成 VNode 时复用(cloneVNode)以减少创建开销
渲染函数执行 → VNode 对象树
当组件首次挂载或响应式数据变化触发更新时,Vue 会执行 render() 函数。该函数内部调用 h()(或 createElement),按参数逐层构造标准的 VNode 对象:
-
type:标识类型(字符串表示原生标签,组件对象表示自定义组件,Fragment/Teleport等特殊类型) -
props:归一化后的属性对象(含事件、指令钩子、class/style 合并结果) -
children:子节点数组,可能是字符串(文本)、VNode、或undefined/空数组 -
key、ref、scopeId等用于 diff 和生命周期管理的元信息
此时得到的是一棵内存中的轻量级对象树,与真实 DOM 完全解耦,具备跨平台能力(如服务端渲染、小程序适配)。
VNode → 真实 DOM(补充说明)
虽然问题聚焦“到 VNode”,但需明确:VNode 本身不操作 DOM。它交由 Vue 的 renderer(渲染器)模块处理。在浏览器环境中,renderer 的 patch 函数对比新旧 VNode 树(Diff 算法),最小化地增删改真实 DOM 节点,并同步更新组件实例的 $el 引用。
这一阶段属于“VNode 的消费”,而非“生成”,但它是整个路径闭环的关键落点。


















