Appearance
Vue 底层库与编译器生态:@vue/compiler-sfc、runtime、reactivity 分别在做什么
很多人学 Vue 时,最熟的是:
refreactivecomputedwatchdefineProps
但一旦看到这些包名,就会开始发懵:
@vue/reactivity@vue/runtime-core@vue/runtime-dom@vue/compiler-core@vue/compiler-dom@vue/compiler-sfc
这时候最容易出现的误解是:Vue 不就是一个框架吗,为什么还要拆这么多包?
更贴近实际的情况是:Vue3 不只是一个“单块框架”,而是一套被拆成多个职责模块的运行时与编译器生态。
1. 为什么 Vue 要拆成这么多底层包
如果把 Vue 只看成“页面模板 + 响应式 API”,很多东西会连不起来。
实际上一个 Vue 应用至少要完成这几类工作:
- 管状态和依赖追踪
- 创建组件实例
- 渲染虚拟 DOM
- 把虚拟 DOM 挂到浏览器真实 DOM
- 把模板编译成渲染函数
- 把
.vue单文件组件拆开处理
这些事情如果全塞进一个大包里,确实也能工作,但:
- 职责不清
- 复用性差
- 工具链对接不自然
- 不利于不同运行时复用核心能力
所以 Vue3 的一个重要工程化方向就是:把响应式、运行时、平台适配层、编译器、单文件组件处理拆成职责更清晰的包。
2. 先用一句话区分这几层
这几层可以分开看:
@vue/reactivity:响应式核心@vue/runtime-core:与平台无关的运行时核心@vue/runtime-dom:浏览器 DOM 平台适配层@vue/compiler-core:平台无关的编译器核心@vue/compiler-dom:面向浏览器模板的编译器扩展@vue/compiler-sfc:处理.vue单文件组件的编译器
如果只压缩成一句话:reactivity 管数据联动,runtime 管组件运行,compiler 管模板变代码,compiler-sfc 管 .vue 文件拆解和编译。
3. @vue/reactivity 是什么
@vue/reactivity 可以看成 Vue3 响应式系统最核心的一层独立能力。
它主要负责:
- 依赖收集
- 触发更新
refreactivecomputedeffect
也就是说,当你写:
ts
const count = ref(0)
const doubled = computed(() => count.value * 2)背后最核心的响应式逻辑,主要就是这层在提供能力。
3.1 它解决什么问题
它解决的是 状态变化后,依赖这份状态的逻辑怎么自动重新执行。
这层本身并不直接关心:
- 组件模板长什么样
- DOM 怎么更新
- 页面怎么挂载
这里的边界要看清:@vue/reactivity 是 Vue 的响应式基础设施,但它本身不等于完整 Vue 框架。`
4. @vue/runtime-core 是什么
@vue/runtime-core 可以看成 Vue 运行时里不直接依赖浏览器平台的一层核心能力。
它主要负责:
- 组件实例模型
- 组件生命周期调度
- 虚拟 DOM 节点组织
- 渲染流程的核心抽象
- 应用实例的一部分核心逻辑
这层最重要的意义是:它更偏框架内核,但尽量不把自己绑死在浏览器 DOM 上。
也就是说,它描述的是:
- 组件怎么跑
- vnode 怎么组织
- 更新怎么调度
而不是直接去操作浏览器里的真实元素。
5. @vue/runtime-dom 是什么
如果说 runtime-core 更像抽象内核,那 @vue/runtime-dom 就可以看成:
把 Vue 运行时接到浏览器 DOM 上的那一层。
它主要负责:
- DOM 节点创建
- 属性、样式、事件更新
- 浏览器平台挂载细节
createApp在 DOM 环境里的落地
把分工拉开看:
runtime-core更偏平台无关runtime-dom更偏浏览器平台实现
这也是为什么 Vue3 不是只能理解成“浏览器模板框架”,而是一套:核心运行时 + 平台适配层
的设计。
6. @vue/compiler-core 是什么
@vue/compiler-core 可以看成 Vue 编译器里与具体平台无关的通用核心。
它主要在做:
- 模板解析
- AST 转换
- 代码生成
如果换一种更容易理解的话说,它负责把:模板描述
逐步变成:渲染函数代码
但这层还尽量不去关心浏览器特有的 DOM 细节。
7. @vue/compiler-dom 是什么
@vue/compiler-dom 是建立在 compiler-core 之上的浏览器模板编译扩展。
可以看成 让 Vue 模板真正适配 DOM 场景的一层编译能力。
它主要负责把浏览器模板相关语法和平台细节补进去,例如:
- DOM 节点相关处理
- 指令在 DOM 语境下的编译
- 浏览器平台特有优化
所以这两层的关系可以记成:
compiler-core:通用编译器骨架compiler-dom:浏览器模板场景的具体扩展
8. @vue/compiler-sfc 是什么
这是你刚提到的重点,也是很多人最容易混的一个包。
@vue/compiler-sfc 可以看成 专门处理 .vue 单文件组件的一层编译器。
所谓 SFC,就是 Single File Component,也就是:
vue
<template>
<div>{{ msg }}</div>
</template>
<script setup lang="ts">
const msg = 'hello'
</script>
<style scoped>
div { color: red; }
</style>这种把模板、脚本、样式放在一个文件里的组件形式。
8.1 它主要负责什么
@vue/compiler-sfc 最核心的职责通常包括:
- 解析
.vue文件结构 - 拆出
template、script、style - 处理
script setup - 生成对应的编译结果和中间信息
- 配合构建工具完成热更新和源码映射
这里的边界也要看清:compiler-sfc 不是单纯把模板编译一下,它是在处理 .vue 文件这种特殊文件格式。
8.2 它和 compiler-dom 的关系
很多人容易把这两个混成同一个东西。
更准确的关系是:
compiler-sfc把.vue文件拆开- 其中
template部分再交给模板编译链路处理 - 这条模板编译链路里会用到
compiler-core、compiler-dom
也就是说:compiler-sfc 更像总入口,compiler-dom 更像模板部分的具体编译器。
如果把 .vue 文件从源码到运行时的过程单独画出来,可以这样看:
mermaid
flowchart TD
A[App.vue] --> B[@vue/compiler-sfc 解析]
B --> C[拆出 template]
B --> D[拆出 script 或 script setup]
B --> E[拆出 style]
C --> F[@vue/compiler-dom]
F --> G[render 函数代码]
D --> H[组件逻辑代码]
E --> I[样式处理链路]
G --> J[@vue/runtime-core]
H --> J
J --> K[@vue/runtime-dom]
K --> L[浏览器真实 DOM]
I --> L9. 一张总图看清包之间的关系
mermaid
flowchart TD
A[.vue 单文件组件] --> B[@vue/compiler-sfc]
B --> C[template]
B --> D[script script setup]
B --> E[style]
C --> F[@vue/compiler-dom]
F --> G[@vue/compiler-core]
G --> H[渲染函数代码]
H --> I[@vue/runtime-core]
I --> J[@vue/runtime-dom]
K[@vue/reactivity] --> I这张图最重要的作用是把三条线拆开:
.vue文件处理线- 模板编译线
- 运行时执行线
10. .vue 文件到底是怎么变成页面的
这是最值得系统讲清楚的一条主线。
大致过程就是这样:
- 你写一个
.vue文件 - 构建工具调用
@vue/compiler-sfc解析它 template被编译成渲染函数script/script setup被转成组件逻辑代码style被提取、作用域处理或注入- 最终生成可被打包器处理的 JavaScript 模块
- 运行时再交给
runtime-core和runtime-dom去创建组件、生成 vnode、更新真实 DOM
如果压缩成一句话:compiler-sfc 负责把 .vue 文件拆成运行时能理解的代码,runtime 再负责把这些代码真正跑起来。
11. vue 这个包和这些底层包是什么关系
这也是一个高频混淆点。
日常业务开发时,我们通常写的是:
ts
import { ref, reactive, createApp } from 'vue'这里的 vue 更像一个面对应用开发者的统一入口包。
也就是说,很多底层能力最后会通过 vue 暴露给你使用。
放回原位看,这层关系是:
vue是开发者最常直接依赖的入口- 底层实际能力由多个内部包协同完成
12. 为什么理解这些底层包有价值
这不是为了背 npm 包名,而是为了把很多工程问题讲通。
例如:
script setup为什么需要编译阶段支持.vue文件为什么离不开构建工具- 为什么有些能力属于运行时,有些能力属于编译时
- 为什么 Vue3 能做更细粒度的编译优化
如果不理解这条线,很多问题就会停留在:感觉 Vue 会自动处理。
13. 和 Vite、插件生态是什么关系
在真实工程里,你通常不是直接手动调用 @vue/compiler-sfc。
更常见的情况是:
Vite@vitejs/plugin-vue
这类工具在底层帮你接好了 .vue 文件处理链路。
也就是说:
- 业务开发者写
.vue - 构建工具和插件调用 Vue 编译器生态
- 最终产出浏览器可运行代码
所以 compiler-sfc 这类底层包虽然平时不常直接手写调用,但它在工程里非常关键。
14. 工程里最容易踩的坑
14.1 把 Vue 理解成纯运行时框架
这样就很难理解:
- 模板为什么能变代码
script setup为什么能工作.vue为什么必须配合构建工具
14.2 把 compiler-sfc 和 compiler-dom 混为一谈
前者处理的是 .vue 文件结构,后者处理的是模板编译到 DOM 渲染函数的那条线。
14.3 把 reactivity 当成完整 Vue
它是核心能力之一,但它本身并不等于整个框架。
14.4 不区分编译时和运行时
这会让很多 Vue3 的优化点、宏能力和工具链问题看起来像黑盒。
15. 一份检查清单
当你在理解 Vue 底层生态时,至少可以问自己:
- 这项能力属于响应式、运行时,还是编译器
- 这是在
.vue文件解析阶段发生,还是在浏览器运行阶段发生 - 这个包更偏平台无关,还是 DOM 平台实现
- 这个能力最终是通过
vue入口暴露,还是主要给工具链使用
16. 小结
如果把这篇压缩成一句话,可以记住:Vue3 之所以能同时拥有响应式、组件运行时、模板编译和 .vue 单文件组件体验,是因为它背后并不是一个单块框架,而是一组分工明确的底层包在协同工作。
最值得先记住的分工是:
@vue/reactivity管响应式@vue/runtime-core管运行时核心@vue/runtime-dom管浏览器 DOM 落地@vue/compiler-core/@vue/compiler-dom管模板编译@vue/compiler-sfc管.vue单文件组件编译