Vue3 源码解析系列 1:从 Debugger 视角读 Vue
2026年2月17日 03:03
引言
直接把源码当黑盒,或者干巴巴从头读到尾,几乎读不下去。更高效的方式是把源码当作“正在运行的程序”,用断点一层层摸清主流程。
这一篇记录我用经典 markdown.html 示例,跟踪 createApp -> mount -> render 的阅读路径。
初期准备
git clone https://github.com/vuejs/core.git
cd core
pnpm install
pnpm run dev # 生成 dev 版本 Vue
# 用浏览器打开示例
open packages/vue/examples/classic/markdown.html
入口断点:createApp
从 createApp 开始是最稳定的入口,它是 app 创建的第一站。
![]()
渲染对比:从“看见结果”到“找到入口”
先把渲染结果和源码入口对齐,这样断点才更有目标感。
![]()
mount 打断点
mount 是渲染真正开始的地方,后面会进入 render 与 patch。
![]()
主流程:props / data / computed / watch
这个阶段会完成 Options API 的初始化,包括 data 绑定、computed 计算、watch 监听等。
![]()
data 绑定:把 data 暴露到 ctx
当 data() 返回对象后,会被转成响应式,并在 dev 模式下挂到 ctx 以便访问。
![]()
for (const key in data) {
checkDuplicateProperties!(OptionTypes.DATA, key)
// expose data on ctx during dev
if (!isReservedPrefix(key[0])) {
Object.defineProperty(ctx, key, {
configurable: true,
enumerable: true,
get: () => data[key],
set: NOOP,
})
}
}
computed:依赖收集与访问触发
依赖收集断点
![]()
访问触发断点
![]()
在模板里出现:
<div v-html="compiledMarkdown"></div>
就会在渲染时读取 compiledMarkdown,触发 computed 的 getter。完整流程可以拆成:
- 读取
computedOptions - 为每个 computed 添加 getter / setter
- 模板渲染时访问
compiledMarkdown - 触发 getter,开始依赖追踪
一句话总结:把一个 getter(或 get/set)包装成带缓存的响应式 ref,并在依赖变化时标记为脏。
export function computed(getterOrOptions, debugOptions, isSSR = false) {
// 1. 解析 getter / setter
let getter, setter
if (isFunction(getterOrOptions)) {
getter = getterOrOptions
} else {
getter = getterOrOptions.get
setter = getterOrOptions.set
}
// 2. 创建 ComputedRefImpl 实例
const cRef = new ComputedRefImpl(getter, setter, isSSR)
// 3. dev 环境下注入调试钩子
if (__DEV__ && debugOptions && !isSSR) {
cRef.onTrack = debugOptions.onTrack
cRef.onTrigger = debugOptions.onTrigger
}
// 4. 返回一个 ref(带 value getter/setter)
return cRef
}
生命周期注册
生命周期函数在这里统一注册,方便后续统一触发。
![]()
registerLifecycleHook(onBeforeMount, beforeMount)
registerLifecycleHook(onMounted, mounted)
registerLifecycleHook(onBeforeUpdate, beforeUpdate)
registerLifecycleHook(onUpdated, updated)
registerLifecycleHook(onActivated, activated)
registerLifecycleHook(onDeactivated, deactivated)
registerLifecycleHook(onErrorCaptured, errorCaptured)
registerLifecycleHook(onRenderTracked, renderTracked)
registerLifecycleHook(onRenderTriggered, renderTriggered)
registerLifecycleHook(onBeforeUnmount, beforeUnmount)
registerLifecycleHook(onUnmounted, unmounted)
registerLifecycleHook(onServerPrefetch, serverPrefetch)
小结
这一篇先把路径跑通:createApp -> mount -> render -> patch -> Options 初始化。后面再深入 patch 和响应式系统时,你会发现思路完全一致:
- 找入口
- 断点追踪
- 明确数据流与调用链
下一篇我会继续浏览更新渲染源码。