VNode 初始化发生在 app.mount() 后首次执行 render 函数时,而非 createApp 阶段;createApp 仅创建应用实例,不生成 VNode;真正构造首个 VNode 树由 renderComponentRoot 完成。

在 Vue 3 的渲染流程中,createApp 阶段本身并不直接触发 VNode 的初始化。VNode 的创建发生在后续的挂载(app.mount())过程中,确切地说,是在首次执行 render 函数时才生成。
createApp 只创建应用实例,不涉及 VNode
createApp 的核心作用是创建一个应用实例(app),它封装了组件配置、插件注册、全局属性等能力,但此时模板尚未编译,DOM 未关联,也**没有 render 函数执行,更没有 VNode 产生**。
- 它返回的
app对象是一个可配置的容器,内部持有rootComponent(通常是用户传入的组件选项对象)和rootProps - 此时组件的
setup、render、模板编译逻辑都未运行 - VNode 是“虚拟节点”的运行时表示,必须依赖响应式系统激活 + 渲染器(renderer)+ 模板或 render 函数才能生成
真正触发 VNode 初始化的时机:mount 时的首次 patch
当调用 app.mount(selector) 后,Vue 才启动真正的渲染流水线:
- 若根组件有
render函数,直接调用它,返回 VNode - 若只有
template,会先通过编译器(runtime compiler)生成render函数,再执行得到 VNode - 接着将该 VNode 传给底层渲染器(如
baseCreateRenderer中的patch),进入“创建真实 DOM → 关联响应式依赖 → 建立更新队列”流程
这个过程中的第一次 render() 调用,才是 VNode 初始化(即首次构造)的明确起点。
立即学习“前端免费学习笔记(深入)”;
Vue 3 组合式 API 与 最佳实践,涵盖代码组织、useXxx 模式、组合式函数设计、Store 集成、响应式优化及功能抽取。
关键入口:renderComponentRoot
在组件级渲染中,VNode 的实际创建由 renderComponentRoot(位于 packages/runtime-core/src/componentRenderUtils.ts)完成。它负责:
- 判断是否已有缓存的
render函数(来自 setup 或 template 编译) - 设置当前渲染上下文(
currentInstance)以便setup和render正确访问响应式数据 - 执行
render()并捕获返回值——这就是首个 VNode 树的根节点
注意:这个函数在 componentUpdateFn(即组件的更新函数)中被首次调用,而该更新函数又在 mountComponent 中被同步注册进调度系统。
小结:流程链路清晰定位
从代码执行顺序看,VNode 初始化严格位于以下路径之后:
-
createApp(Comp)→ 创建 app 实例(无 VNode) -
app.mount(dom)→ 触发mount逻辑 -
createVNode(RootComponent)→ 构造根组件 VNode(轻量,仅描述结构) -
render(rootVNode, rootContainer)→ 进入 renderer,执行patch -
processComponent(...)→ 判断为组件,调用mountComponent -
renderComponentRoot(instance)→ 执行用户 render 函数,返回完整 VNode 树
所以,“CreateApp 阶段触发 VNode 初始化”是一种常见误解;准确说是 mount 启动后,在组件挂载子流程中,由 renderComponentRoot 完成首次 VNode 构造。

















