Vue3 单文件组件(.vue)的渲染过程是一个「从代码解析DOM 挂载」的有序流程,核心围绕「编译阶段」和「运行时阶段」展开

一个 .vue 文件的结构:

<!-- 1. 模板部分 (template) -->
<template>
  <div>{{ msg }}</div>
</template>

<!-- 2. 脚本部分 (script setup) -->
<script setup lang="ts">
import { ref, onMounted } from 'vue'
// 响应式数据
const msg = ref('Hello Vue3')
// 生命周期钩子
onMounted(() => {
  console.log('组件挂载完成')
})
</script>

<!-- 3. 样式部分 (style) -->
<style scoped>
div { color: red; }
</style>

整体渲染流程(宏观):Vue3 处理 .vue 文件分为两大阶段:

一、编译阶段(构建时,如 Vite/webpack 打包):

构建工具(与浏览器无关)把 .vue 文件解析为 JS 模块(浏览器无法直接识别 .vue,需编译);

构建工具(Vite/webpack + vue-loader)会将 .vue 文件拆分为 3 部分处理:

1、template 编译

        把模板字符串编译为「渲染函数(render)」—— 本质是将 <div>{{ msg }}</div> 转为 _createVNode("div", null, msg.value) 这类 VNode(Virtual Node,虚拟节点) 生成代码

2、script 编译

  • 若用 <script setup>,会被编译为「组件 setup 函数」(自动提升顶层变量 / 导入为响应式上下文)
  • 若用传统 <script>,会保留 export default { setup(), ... } 结构

3、style 编译

  • 处理 scoped/module 等属性(如添加 data-v-xxx 作用域标识)
  • 提取样式,最终注入到 HTML 的 <style> 标签(或打包为 CSS 文件)

编译依赖关系链:

                  script 编译
                      ↓ (提供:props、setup 返回值、组件配置)
                  template 编译 ←─┐
                      ↓ (提供:scopeId、CSS 映射)    │
                 style 编译 ←─────┘
                     ↓
                  最终组合

总结

  1. 不是同时编译:三个部分的编译有先后顺序和依赖关系

  2. 主要依赖关系:

    Template 编译需要 Script 的信息(props、setup 返回值等)
    Style 编译需要 Template 生成的 scopeId
  3. 实际执行可能部分并行:现代工具(Vite)会尽可能并行处理,但会处理依赖

  4. 最终是串行组合:所有部分编译完成后,串行组合成最终模块

  5. 编译时信息传递:通过唯一的组件 ID 和 AST 分析来传递信息

注:

        浏览器原生支持的核心语言:HTML、CASS、JavaScript,其他语言不能直接被浏览器识别,需要先转换为 JavaScript 或 WebAssembly。

        浏览器通过 Content-Type 判断如何解析内容:

HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
<!-- 浏览器:这是 HTML,我要解析为 DOM -->

HTTP/1.1 200 OK
Content-Type: application/javascript
<!-- 浏览器:这是 JS,我要执行它 -->

二、运行时阶段(浏览器执行):

        编译后的 .vue 组件被导入并使用(如 import MyComponent from './MyComponent.vue'),编译后的 JS 模块执行,完成组件实例化、渲染、挂载。此时进入运行时执行流程

1:组件实例化准备

        Vue 内部创建组件实例,初始化组件上下文(props、slots、attrs 等),为后续执行做准备。(无用户可见输出,Vue 内部完成)

2:执行 <script setup> 代码(setup 函数执行)

        执行 <script setup> 内的顶层代码,包括导入、数据声明、钩子注册、同步代码执行。

        在 Vue3 的 <script setup> 语法中,「setup 顶层代码」指的是 <script setup> 标签内所有「不在函数 / 钩子内部」的直接执行代码—— 这些代码会在组件实例化时、setup 函数执行阶段「最先且仅执行一次」,是组件运行时第一个执行的用户代码。顶层代码中声明的变量、函数、导入的组件,会自动暴露给模板(无需手动 return,这是 <script setup> 的核心语法糖)

<script setup>
import { ref, onBeforeMount, onMounted } from 'vue' // ① 顶层导入
const msg = ref('Hello Vue3') // ② 声明响应式数据
onBeforeMount(() => console.log('挂载前')) // ③ 注册生命周期钩子
onMounted(() => console.log('挂载完成')) // ③ 注册生命周期钩子
console.log('setup 执行,msg:', msg.value) // ④ 执行顶层同步代码
</script>
setup 执行,msg: Hello Vue3

  误区 :

        把「钩子注册」当成「钩子执行」

<script setup>
// 顶层代码:仅「注册」onMounted 钩子,不会立即执行内部代码
onMounted(() => {
  console.log('mounted 执行') // 这行要等 DOM 挂载后才执行
})
// 顶层代码:立即执行
console.log('顶层代码执行')
</script>

3:执行渲染函数(生成 VNode)

  • 操作描述:调用编译后的 render 函数,结合 setup 上下文的响应式数据生成 VNode。
  • 编译后 render 函数示例(Vue 内部生成):
    function render(_ctx, _cache) {
      return _createVNode("div", null, _ctx.msg)
    }
    
  • 输出示例:(无直接控制台输出,Vue 内部生成 VNode 数据结构)

4:VNode 转换为真实 DOM

  • 操作描述:Vue 内部通过 patch 算法将 VNode 转换为真实 DOM 节点(未挂载到页面)。
  • 输出示例:(无用户可见输出,Vue 内部完成 DOM 节点创建)

5:触发 beforeMount 钩子

  • 操作描述:执行注册的 onBeforeMount 钩子函数。
  • 输出示例
    挂载前
    

6:真实 DOM 挂载到页面

  • 操作描述:将创建好的真实 DOM 节点插入到指定的挂载容器(如 #app)。
  • 输出示例:(页面上可见 <div>Hello Vue3</div> 元素,无控制台输出)

7:触发 mounted 钩子

        操作描述:执行注册的 onMounted 钩子函数,可操作真实 DOM。

        代码示例(钩子内操作 DOM):

onMounted(() => {
  const dom = document.querySelector('div')
  console.log('挂载完成,DOM 内容:', dom.innerText)
})

        输出示例

挂载完成,DOM 内容: Hello Vue3

8:样式生效

  • 操作描述:编译后的 CSS 样式(如 scoped 样式)应用到 DOM 节点。
  • 代码示例
    <style scoped>
    div { color: red; }
    </style>
    
  • 输出示例:(页面上 <div> 文字变为红色,无控制台输出)

整体控制台输出顺序总结

plaintext

setup 执行,msg: Hello Vue3
挂载前
挂载完成,DOM 内容: Hello Vue3

页面上则依次完成 DOM 挂载和样式渲染,最终显示红色的「Hello Vue3」文本。

「setup 顶层代码」指的是 <script setup> 标签内所有「不在函数 / 钩子内部」的直接执行代码—— 这些代码会在组件实例化时、setup 函数执行阶段「最先且仅执行一次」,是组件运行时第一个执行的用户代码。

补充:响应式更新时的执行顺序(非首次渲染)

若后续响应式数据变化(如 msg.value = 'New Value'),执行顺序变为:

  1. 响应式数据触发依赖收集 → 重新执行渲染函数,生成新的 VNode;
  2. Vue 对比新旧 VNode(diff 算法),计算最小更新量;
  3. 更新真实 DOM(仅更新变化的部分);
  4. 触发 onUpdated 钩子。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐