Vue3 源码解析系列 1:从 Debugger 视角读 Vue

引言

直接把源码当黑盒,或者干巴巴从头读到尾,几乎读不下去。更高效的方式是把源码当作"正在运行的程序",用断点一层层摸清主流程。

这一篇记录我用经典 markdown.html 示例,跟踪 createApp -> mount -> render 的阅读路径。

初期准备

bash 复制代码
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 是渲染真正开始的地方,后面会进入 renderpatch

主流程:props / data / computed / watch

这个阶段会完成 Options API 的初始化,包括 data 绑定、computed 计算、watch 监听等。

data 绑定:把 data 暴露到 ctx

data() 返回对象后,会被转成响应式,并在 dev 模式下挂到 ctx 以便访问。

js 复制代码
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:依赖收集与访问触发

依赖收集断点

访问触发断点

在模板里出现:

html 复制代码
<div v-html="compiledMarkdown"></div>

就会在渲染时读取 compiledMarkdown,触发 computed 的 getter。完整流程可以拆成:

  1. 读取 computedOptions
  2. 为每个 computed 添加 getter / setter
  3. 模板渲染时访问 compiledMarkdown
  4. 触发 getter,开始依赖追踪

一句话总结:把一个 getter(或 get/set)包装成带缓存的响应式 ref,并在依赖变化时标记为脏。

js 复制代码
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
}

生命周期注册

生命周期函数在这里统一注册,方便后续统一触发。

js 复制代码
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 和响应式系统时,你会发现思路完全一致:

  1. 找入口
  2. 断点追踪
  3. 明确数据流与调用链

下一篇我会继续浏览更新渲染源码。

相关推荐
Setsuna_F_Seiei6 小时前
前端转型 Agent 开发 04 之 MCP 与 Skill(赋予 Agent 更广工作能力)
前端·agent
刘发财8 小时前
前端2秒生成500页矢量PDF,rust真的强到没朋友
前端·javascript·rust
郑州光合科技余经理8 小时前
国际版外卖系统:税率字段怎么和订单主流程解耦
android·java·开发语言·前端·后端·php·ai编程
梦想平凡9 小时前
百游棋牌源代码开发搭建教程(十):隔离部署、备份恢复与双端验收
java·前端·javascript·数据库·源代码管理
yume_sibai10 小时前
06-Rust Web 开发实战(Axum 框架 + 数据库 + JWT 认证 + 中间件 + 部署)
前端·数据库·rust
计算机魔术师11 小时前
特朗普上台打给黄仁勋:AI末日论是骗局,我们绝不让它发生
前端
troy12811 小时前
Python 基础语法(八):Web 后端开发、数据分析与可视化、网络爬虫、人工智能 / 大模型应用
前端·python·数据分析
计算机魔术师12 小时前
CEO说要慢下来,黑客说别做梦了——同一篇论文,两种命运
前端
kyriewen12 小时前
我花3天抓了一个幽灵bug,凶手藏在第4层
前端·javascript·程序员
IT_陈寒13 小时前
Java里用Stream.parallel()翻车实录,这性能还不如单线程
前端·人工智能·后端