Vue3 源码课程简介与调试环境搭建
🎯 引言
这是「Vue3 源码」课程的第一篇。这门课不讲 API 怎么用,而是一起打开 Vue3 的源代码,看看你每天调用的 reactive、ref、watchEffect 背后到底发生了什么。别担心跟不上,源码里绕口的概念我们都会翻译成大白话,一次只讲一个点。
学完本篇,你将能独立搭建一套 Vue3 源码的本地调试环境,能在浏览器里对源码打断点、单步跟踪,并且对 vuejs/core 仓库的目录结构有一张清晰的地图,知道去哪一层找你关心的问题。
🧱 为什么要读 Vue3 源码
读源码要花时间,值不值?这一节先回答这个问题。结合日常开发,收益主要在这三件事上。
排查问题更有方向。 你一定遇到过这种情况:改了一个对象的属性,视图却不更新;或者 watch 的回调莫名其妙多触发了一次。只熟悉 API 用法时,这类问题只能靠猜和试。读过响应式原理后,你会知道这些「怪现象」背后的机制,排查时就有明确方向。
写出更合理的代码。 比如理解了数据更新是「攒一批再统一渲染」之后,你就明白为什么连续多次赋值页面只刷新一次。这些不是背下来的规范,而是知道原理后的自然选择。
理解 API 的设计动机。 Vue3 的很多设计(比如 ref 为什么要 .value)初看有些别扭,读懂源码后会明白它们是为了解决什么具体问题而存在的。这种理解也能迁移到其他框架的学习上。
✨ 版本说明
动手之前,先交代清楚我们读的是哪个版本、放在哪里。本课程基于 Vue 3.5.x 的源码讲解,调试环境为 Node.js v22 + pnpm。
Vue3 的源码放在 GitHub 的 vuejs/core 仓库里。打开仓库你会发现它不是一个项目,而是很多个独立的小项目放在一起,这种组织方式叫 monorepo(单仓库多包):响应式、渲染、编译等功能各自是一个独立的包,由 pnpm 统一管理。
这种拆分对读源码很友好:想看响应式,直接进 reactivity 这个包,不用在 Vue2 那样的大仓库里跨目录翻找。如果你之前读过 Vue2 源码课程,会发现核心思想(响应式、虚拟 DOM、模板编译)是相通的,这门课会侧重 Vue3 的变化点。
🧭 源码目录地图
版本清楚了,接下来的疑问是:这么多文件夹,我想看的东西在哪个里面?打开 vuejs/core 仓库的 packages 目录,你会看到按职责拆分的各个包。下面这张目录地图标出了每个包的关键文件,点开文件就能看到它的职责,后面每篇都会深入其中的一部分:
每个包的职责也可以用一张表速览:
| 目录 | 职责 |
|---|---|
packages/vue | Vue 的入口组装,把运行时和编译器打包成你在浏览器里用的 Vue |
packages/runtime-dom | 浏览器平台渲染:DOM 操作、事件绑定、createApp 的 web 版本 |
packages/runtime-core | 平台无关的运行时核心:组件实例、虚拟 DOM、调度器 |
packages/reactivity | 响应式系统:reactive / ref / effect / computed 都在这个包 |
packages/compiler-core | 平台无关的编译器:把模板解析成 AST,再生成 render 函数代码 |
packages/compiler-dom | 浏览器端编译:在 compiler-core 之上补充 DOM 相关的处理 |
packages/shared | 各包共享的工具函数:类型判断、字符串处理等 |
其中 runtime-core 和 reactivity 是本课程的主战场:前者管组件和渲染,后者管整个响应式系统,其他包知道职责即可。最后把主线串起来:你写的模板先由 compiler(编译器) 转成 render 函数,runtime-core(运行时核心) 执行 render 函数把页面渲染出来,数据变化时由 reactivity(响应式系统) 通知页面重新渲染。后面每篇都会深入这条主线上的一个环节。
🛠 调试环境搭建
光说不练效果有限。这一节我们解决一个很实际的问题:怎么在自己电脑上把源码跑起来、还能打断点?整个过程只需要几分钟,之后每篇文章你都可以在真实的源码里验证。
本课程在 Node.js v22 + pnpm 环境下验证通过。先把源码拉下来并切换到 v3.5.13:
# 克隆仓库(本地 Node.js 版本:v22)
git clone https://github.com/vuejs/core.git vue3-source
cd vue3-source
# 切换到 v3.5.13,保证你看到的代码和课程一致
git checkout v3.5.13
# 安装依赖(monorepo 必须用 pnpm,包版本以仓库 pnpm-lock.yaml 为准)
pnpm install
workspace: 协议互相引用(可以简单理解为「从本仓库内部找依赖」),用 npm 安装会无法识别。--legacy-peer-deps 之类的宽松参数,不影响后续调试。安装完成后,构建一份带 sourcemap 的开发版本:
# 构建开发版 vue.js(带完整警告和 sourcemap,适合调试)
pnpm run dev
这里出现一个新名词 sourcemap(源码映射)。构建会把许多源码文件打包合并成一个文件,代码位置全都对不上了。sourcemap 就像一张「对照表」,浏览器靠它把打包后的代码还原回原始源码,你打断点时看到的就是源码文件本身。
构建完成后,产物在 packages/vue/dist/vue.global.js。接着在 packages/vue/examples 目录下新建一个调试页面,比如 packages/vue/examples/debug/01-hello.html:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>Vue3 源码调试</title>
</head>
<body>
<div id="app"></div>
<script src="../../dist/vue.global.js"></script>
<script>
const { createApp, ref } = Vue;
const app = createApp({
setup() {
const count = ref(0);
// 两秒后修改数据,方便观察更新流程
setTimeout(() => {
// debugger 是 JS 自带的关键字:打开开发者工具时,代码执行到这一行会自动暂停
debugger;
count.value++;
}, 2000);
return { count };
},
template: `<div>count: {{ count }}</div>`,
});
app.mount('#app');
</script>
</body>
</html>
<script setup>?它需要构建工具参与编译,而这个纯 HTML 调试页没有编译环节,直接手写 setup() 函数反而更贴近源码真实处理的逻辑。用浏览器直接打开这个 HTML 文件,打开开发者工具(F12),页面会在 debugger 处自动停住。然后你就可以:
- 在 Sources 面板中找到
packages/reactivity/src/ref.ts,sourcemap 已经把构建产物映射回了源码。 - 在
ReactiveEffect、triggerRefValue等函数里打上断点(点一下行号即可)。 - 单步执行,观察
count.value修改后触发的调用链。
这就是你未来读源码的标准动作:找到关键函数 → 打断点 → 单步看数据和调用栈。
pnpm run dev 构建的是带 sourcemap 的开发版,适合调试。生产构建(pnpm run build)会压缩代码、去掉警告,不适合学习源码。💡 阅读方法建议
环境搭好了,怎么读才不迷路?这一节分享几条实际读源码时比较有用的习惯。
- 带着具体问题去读。 不要从第一行开始通读。比如「改了
ref的.value,视图是怎么更新的?」带着这个问题,你自然会找到对应的那条函数调用链。 - 从入口往下追。 先找到
createApp的入口(packages/runtime-dom/src/index.ts),再顺着挂载、渲染的流程一层层深入,比漫无目的地翻文件高效得多。 - 配合手写迷你实现验证。 读到一个机制后,自己动手写一个几十行的简化版(比如迷你响应式),能写对才是真的懂了。本课程多篇都会安排这样的练习。
- 跳过 TS 类型与边缘分支。 Vue3 源码是 TypeScript 写的,也就是给变量和函数标注了类型信息,阅读时可以跳过不看。课程中的源码引用也做了教学简化,去掉了类型注解和非核心分支。
🗺 课程路线图
环境和方法都齐了,最后看一眼完整路线。本课程共 8 篇,按「先响应式、再运行时、最后编译」的顺序展开:
- 简介与环境搭建(本篇):目录地图与调试环境。
- createApp 与挂载流程:从
createApp到组件渲染到页面上,发生了什么。 - Proxy 响应式原理:
reactive如何拦截你对数据的读写。 - effect 与依赖收集触发:Vue 如何记住「谁用到了这个数据」,数据变了又如何通知。
- ref 与 computed 原理:
.value的拦截机制与 computed 的懒计算。 - 调度器与 nextTick:批量更新、异步排队是怎么实现的。
- 虚拟 DOM 与 patch:页面更新时,Vue 如何只改动变化的部分。
- 模板编译:模板 → AST → render 函数的三段式流程。
<script setup>、响应式 API)。如果还没系统学过,建议先完成 Vue3 使用课程。读过 Vue2 源码课程 更佳,可以对比理解两代的差异。🧾 小节总结
- 读 Vue3 源码的价值在于:排查问题更有方向、写出更合理的代码、理解 API 设计动机。
- 课程基于 Vue 3.5.x,源码在 vuejs/core 仓库,是 pnpm 管理的 monorepo(一个仓库里放多个独立包),调试环境为 Node.js v22 + pnpm。
- 源码目录中,
runtime-core和reactivity是主线,其他包按需了解即可。 - 调试环境三步走:克隆仓库并切到 v3.5.13 →
pnpm run dev构建开发版 → 在packages/vue/examples里写页面用浏览器断点调试(sourcemap 会让断点停在原始源码上)。 - 读源码的有效方法是:带着具体问题、从入口往下追、配合手写迷你实现验证、跳过 TS 类型与边缘分支。
❓ 知识问答
Q1:为什么 Vue3 源码要拆成多个 package?
A:拆包后每个模块职责清晰、边界明确。比如响应式系统独立成 reactivity 包后,阅读时只需专注这一个包,不用关心其他部分的实现。
Q2:我不会用浏览器的断点调试,影响学习吗?
A:不影响读文章本身,但建议花几分钟熟悉 Chrome DevTools 的 Sources 面板,学会打断点和单步执行。这是读源码的基本工具,之后会频繁用到。
Q3:pnpm run dev 和 pnpm run build 有什么区别?
A:pnpm run dev 构建带 sourcemap 的未压缩版本,保留完整警告信息,适合调试学习。pnpm run build 构建压缩后的生产版本,去掉了警告,不适合读源码。
Q4:课程中引用的源码和仓库里的代码完全一样吗?
A:做了教学简化。去掉了 TypeScript 类型注解和一些边缘分支,保留核心逻辑。每处引用都会标注「简化自」哪个文件,方便你对照原始代码。
Q5:读过 Vue2 源码课程,这门课会重复吗?
A:核心思想(响应式、虚拟 DOM、模板编译)是相通的,但实现差异很大。这门课侧重这些变化点,相同的地方只做简要回顾。
🧪 小练习
搭建好环境后,试着完成这个练习:在 packages/vue/examples/debug/01-hello.html 中,给页面再加一个 ref 字段 message 并渲染到页面上,然后在浏览器断点中观察修改 .value 时触发的调用链。
const { createApp, ref } = Vue;
const app = createApp({
setup() {
const count = ref(0);
// 请在这里编写代码:声明一个名为 message 的 ref
setTimeout(() => {
debugger;
// 请在这里编写代码:修改 message 的值
}, 2000);
return { count };
},
// 请在这里编写代码:把 message 也渲染到模板里
template: `<div>count: {{ count }}</div>`,
});
完成后在 Sources 面板中找到 packages/reactivity/src/ref.ts,在 triggerRefValue 处打断点,观察一次修改触发了哪些函数调用。
🎉 恭喜你已经搭好 Vue3 源码的调试环境,也有了完整的课程地图!下一篇我们从 createApp 开始,一步步跟踪应用从创建到挂载的完整流程。
