Vue3源码解读(一):响应式系统 reactive/ref 核心原理图解(2026最新版)

Vue3源码解读(一):响应式系统 reactive/ref 核心原理图解(2026最新版) 本文是 Vue3源码系列教程从基础到进阶附完整代码示例收藏备用一、前言Vue3 的响应式系统是其最核心的特性之一相比 Vue2 的 Object.definePropertyVue3 使用 Proxy 实现了更强大、更灵活的响应式能力。本文将深入源码带你彻底理解 reactive 和 ref 的实现原理。学完本文你将掌握Proxy 与 Reflect 的基本用法reactive 的创建过程与依赖收集ref 的包装原理与 .value 访问effect 副作用函数的执行机制手写一个极简版响应式系统二、Vue3 响应式系统概览2.1 核心 APIimport { reactive, ref, computed, effect } from vue // reactive: 创建对象的响应式代理 const state reactive({ count: 0 }) // ref: 创建值的响应式引用 const count ref(0) // computed: 创建计算属性 const double computed(() count.value * 2) // effect: 注册副作用函数 effect(() { console.log(count changed:, count.value) })2.2 响应式系统架构┌─────────────────────────────────────────┐ │ 用户代码 (Component) │ ├─────────────────────────────────────────┤ │ reactive() │ ref() │ computed() │ ├─────────────────────────────────────────┤ │ Proxy 代理对象 (Handlers) │ ├─────────────────────────────────────────┤ │ 依赖收集 (track) / 触发更新 (trigger) │ ├─────────────────────────────────────────┤ │ Effect 副作用函数队列 │ └─────────────────────────────────────────┘三、Proxy 与 Reflect 基础3.1 为什么用 Proxy 替代 Object.defineProperty特性Object.definePropertyProxy拦截能力仅 get/set13种操作新增属性无法监听自动监听数组索引需特殊处理原生支持性能递归遍历懒代理兼容性IE9IE不支持3.2 Proxy 基本用法const target { name: Vue, version: 3 } const proxy new Proxy(target, { // 拦截读取操作 get(target, key, receiver) { console.log(get:, key) return Reflect.get(target, key, receiver) }, // 拦截设置操作 set(target, key, value, receiver) { console.log(set:, key, , value) return Reflect.set(target, key, value, receiver) }, // 拦截 in 操作符 has(target, key) { console.log(has:, key) return Reflect.has(target, key) }, // 拦截 delete 操作 deleteProperty(target, key) { console.log(delete:, key) return Reflect.deleteProperty(target, key) } }) proxy.name // get: name → Vue proxy.version 4 // set: version 4 → true name in proxy // has: name → true delete proxy.version // delete: version → true3.3 Reflect 的作用Reflect 提供了一套与 Proxy 处理器对应的方法确保 this 指向正确const obj { _value: 0, get value() { return this._value } } const proxy new Proxy(obj, { get(target, key, receiver) { // receiver 是 proxy 本身 // 使用 Reflect.get 确保 getter 中的 this 指向 proxy return Reflect.get(target, key, receiver) } })四、reactive 源码解析4.1 创建 reactive 对象// 简化版实现 const reactiveMap new WeakMap() // 缓存已创建的代理 function reactive(target) { // 1. 检查是否是对象 if (!isObject(target)) { console.warn(value cannot be made reactive: ${String(target)}) return target } // 2. 检查是否已有代理 if (reactiveMap.has(target)) { return reactiveMap.get(target) } // 3. 创建 Proxy 代理 const proxy new Proxy(target, mutableHandlers) // 4. 缓存代理对象 reactiveMap.set(target, proxy) return proxy } function isObject(val) { return val ! null typeof val object }4.2 处理器对象 mutableHandlersconst mutableHandlers { get(target, key, receiver) { // 收集依赖 track(target, key) const result Reflect.get(target, key, receiver) // 递归代理嵌套对象懒代理 if (isObject(result)) { return reactive(result) } return result }, set(target, key, value, receiver) { const oldValue target[key] const result Reflect.set(target, key, value, receiver) // 值发生变化时触发更新 if (oldValue ! value) { trigger(target, key) } return result } }4.3 依赖收集 track 与触发 trigger// 存储依赖关系的数据结构 const targetMap new WeakMap() let activeEffect null // 当前正在执行的 effect // 收集依赖 function track(target, key) { if (!activeEffect) return // 获取 target 对应的 depsMap let depsMap targetMap.get(target) if (!depsMap) { depsMap new Map() targetMap.set(target, depsMap) } // 获取 key 对应的 dep (Set) let dep depsMap.get(key) if (!dep) { dep new Set() depsMap.set(key, dep) } // 收集当前 effect dep.add(activeEffect) // 建立双向引用用于清理 activeEffect.deps.push(dep) } // 触发更新 function trigger(target, key) { const depsMap targetMap.get(target) if (!depsMap) return const dep depsMap.get(key) if (dep) { // 执行所有相关的 effect dep.forEach(effect { if (effect.scheduler) { effect.scheduler() // 异步调度 } else { effect.run() // 同步执行 } }) } }五、ref 源码解析5.1 ref 的实现原理ref 用于将基本类型包装成响应式对象function ref(value) { return createRef(value, false) } function createRef(rawValue, shallow) { if (isRef(rawValue)) { return rawValue } return new RefImpl(rawValue, shallow) } class RefImpl { constructor(value, __v_isShallow) { this.__v_isShallow __v_isShallow this._value __v_isShallow ? value : toReactive(value) this._rawValue value } get value() { // 收集依赖 trackRefValue(this) return this._value } set value(newVal) { if (hasChanged(newVal, this._rawValue)) { this._rawValue newVal this._value this.__v_isShallow ? newVal : toReactive(newVal) // 触发更新 triggerRefValue(this) } } } function toReactive(value) { return isObject(value) ? reactive(value) : value } function isRef(r) { return !!(r r.__v_isRef true) } function hasChanged(value, oldValue) { return !Object.is(value, oldValue) }5.2 ref 的依赖收集const refMap new WeakMap() function trackRefValue(ref) { if (activeEffect) { trackEffects(refMap.get(ref) || (refMap.set(ref, (new Set())), refMap.get(ref))) } } function triggerRefValue(ref) { const dep refMap.get(ref) if (dep) { triggerEffects(dep) } }5.3 ref 与 reactive 的区别// reactive: 代理整个对象 const state reactive({ count: 0 }) state.count // 直接修改属性 // ref: 包装单个值 const count ref(0) count.value // 通过 .value 访问 // ref 可以包装对象自动用 reactive 转换 const user ref({ name: Tom }) user.value.name Jerry // 响应式更新六、effect 副作用函数6.1 effect 的基本实现function effect(fn, options {}) { const _effect new ReactiveEffect(fn, options.scheduler) if (!options.lazy) { _effect.run() // 立即执行 } const runner _effect.run.bind(_effect) runner.effect _effect return runner } class ReactiveEffect { constructor(fn, scheduler) { this.fn fn this.scheduler scheduler this.deps [] // 收集的依赖集合 this.active true } run() { if (!this.active) { return this.fn() } // 设置当前激活的 effect activeEffect this try { return this.fn() } finally { activeEffect null } } stop() { if (this.active) { cleanupEffect(this) this.active false } } } // 清理 effect 的所有依赖 function cleanupEffect(effect) { effect.deps.forEach(dep { dep.delete(effect) }) effect.deps.length 0 }6.2 effect 的使用示例const count ref(0) // 创建 effect const stop effect(() { console.log(count is:, count.value) }) // 输出: count is: 0 count.value // 输出: count is: 1 count.value // 输出: count is: 2 // 停止 effect stop() count.value // 不再输出6.3 调度器 schedulerconst count ref(0) effect(() { console.log(count:, count.value) }, { // 自定义调度使用微任务队列 scheduler: (fn) { Promise.resolve().then(fn) } }) count.value 1 count.value 2 count.value 3 // 输出: count: 0 // 只输出一次: count: 3合并了多次更新七、手写极简版响应式系统// 完整实现 let activeEffect null const targetMap new WeakMap() // 收集依赖 function track(target, key) { if (!activeEffect) return let depsMap targetMap.get(target) if (!depsMap) { depsMap new Map() targetMap.set(target, depsMap) } let dep depsMap.get(key) if (!dep) { dep new Set() depsMap.set(key, dep) } dep.add(activeEffect) } // 触发更新 function trigger(target, key) { const depsMap targetMap.get(target) if (!depsMap) return const dep depsMap.get(key) if (dep) { dep.forEach(effect effect()) } } // 创建响应式对象 function reactive(target) { return new Proxy(target, { get(target, key, receiver) { track(target, key) return Reflect.get(target, key, receiver) }, set(target, key, value, receiver) { const result Reflect.set(target, key, value, receiver) trigger(target, key) return result } }) } // 创建 ref function ref(value) { const refObject { get value() { track(refObject, value) return value }, set value(newValue) { value newValue trigger(refObject, value) } } return refObject } // 注册 effect function effect(fn) { activeEffect fn fn() activeEffect null } // 使用示例 const state reactive({ count: 0 }) effect(() { console.log(state.count , state.count) }) state.count // 输出: state.count 1 state.count // 输出: state.count 2 const num ref(10) effect(() { console.log(num.value , num.value) }) num.value 20 // 输出: num.value 20八、computed 计算属性8.1 computed 的实现function computed(getter) { let value let dirty true // 标记是否需要重新计算 const effectFn effect(getter, { lazy: true, scheduler: () { dirty true // 依赖变化时标记为脏 } }) return { get value() { if (dirty) { value effectFn() dirty false } return value } } } // 使用 const count ref(0) const double computed(() count.value * 2) console.log(double.value) // 0 count.value 5 console.log(double.value) // 10九、常见问题与解决方案❌ 问题1解构 reactive 对象失去响应式const state reactive({ count: 0, name: Vue }) // ❌ 错误解构后失去响应式 const { count } state count // 不会触发更新 // ✅ 正确使用 toRefs import { toRefs } from vue const { count, name } toRefs(state) count.value // 响应式更新❌ 问题2数组索引和 length 修改不触发更新const arr reactive([1, 2, 3]) // ✅ Vue3 支持以下操作 arr[0] 10 // 触发更新 arr.length 0 // 触发更新 arr.push(4) // 触发更新❌ 问题3reactive 不能直接替换整个对象let state reactive({ count: 0 }) // ❌ 错误替换后失去响应式连接 state { count: 1 } // ✅ 正确修改原对象的属性 state.count 1 // 或改用 ref const stateRef ref({ count: 0 }) stateRef.value { count: 1 } // 正常响应❌ 问题4异步获取数据后赋值不更新const state reactive({ list: [] }) // ❌ 可能不触发更新如果 list 一开始不存在 async function fetchData() { state.list await fetch(/api/list) } // ✅ 确保属性已定义 const state reactive({ list: [] }) // 初始化时定义 // 或使用 ref const list ref([]) async function fetchData() { list.value await fetch(/api/list) }十、总结Vue3 响应式系统的核心设计Proxy 代理拦截对象操作实现深度响应式懒代理只在访问属性时才递归创建代理依赖收集通过 track 在读取时收集 effect触发更新通过 trigger 在修改时执行 effectref 包装用对象包裹基本类型通过 .value 访问性能优化要点大数据列表使用shallowRef或shallowReactive减少代理开销避免不必要的嵌套响应式对象合理使用computed缓存计算结果觉得有用的话点个赞收藏关注我每周持续更新实战教程标签vue3 | 源码 | 响应式 | reactive | ref | proxy