Vue3 的响应式系统源码级执行流程解析
字数 577 2025-11-13 18:24:01
Vue3 的响应式系统源码级执行流程解析
描述:Vue3 的响应式系统基于 Proxy 实现,当数据被访问时自动收集依赖,数据被修改时自动触发更新。我们将深入源码级别的执行流程,从 reactive 函数创建代理对象开始,到 track 依赖收集、trigger 触发更新的完整过程。
解题过程:
-
reactive 函数创建响应式对象
- 当调用
reactive(target)时,首先检查目标对象是否已经是响应式对象(通过ReactiveFlags.IS_REACTIVE标记) - 如果不是,则调用
createReactiveObject()函数:
function createReactiveObject(target) { // 1. 检查是否只读对象或非对象类型 if (!isObject(target)) return target // 2. 如果已经是响应式对象,直接返回 if (target[ReactiveFlags.IS_REACTIVE]) return target // 3. 检查是否已有存在的代理对象(避免重复代理) const existingProxy = proxyMap.get(target) if (existingProxy) return existingProxy // 4. 创建 Proxy 代理对象 const proxy = new Proxy( target, baseHandlers // 使用基础的 handlers(mutableHandlers) ) // 5. 缓存代理对象到 WeakMap 中 proxyMap.set(target, proxy) return proxy } - 当调用
-
Proxy handlers 的 get 陷阱实现
- 当访问响应式对象属性时,触发 get 陷阱:
const mutableHandlers = { get(target, key, receiver) { // 1. 检查特殊标记(如 __v_isReactive) if (key === ReactiveFlags.IS_REACTIVE) return true // 2. 执行原始获取操作 const res = Reflect.get(target, key, receiver) // 3. 依赖收集(关键步骤) track(target, key) // 4. 如果值是对象,递归转换为响应式 if (isObject(res)) { return reactive(res) } return res } } -
track 依赖收集详细流程
- track 函数建立数据与副作用函数(effect)的关联:
function track(target, key) { // 1. 检查是否应该收集依赖(有活跃的 effect) if (!activeEffect || !shouldTrack) return // 2. 获取 target 对应的依赖映射(depsMap) let depsMap = targetMap.get(target) if (!depsMap) { targetMap.set(target, (depsMap = new Map())) } // 3. 获取 key 对应的依赖集合(dep) let dep = depsMap.get(key) if (!dep) { depsMap.set(key, (dep = new Set())) } // 4. 将当前活跃的 effect 添加到依赖集合 if (!dep.has(activeEffect)) { dep.add(activeEffect) // 同时 effect 也记录自己属于哪些 dep(用于清理) activeEffect.deps.push(dep) } } -
Proxy handlers 的 set 陷阱实现
- 当修改响应式对象属性时,触发 set 陷阱:
const mutableHandlers = { set(target, key, value, receiver) { // 1. 检查是否是新增属性还是修改已有属性 const hadKey = hasOwn(target, key) const oldValue = target[key] // 2. 执行原始设置操作 const result = Reflect.set(target, key, value, receiver) // 3. 只有当目标对象就是 receiver 的代理目标时才触发更新 if (target === toRaw(receiver)) { if (!hadKey) { // 新增属性触发 ADD 类型的更新 trigger(target, key, TriggerOpTypes.ADD, value) } else if (hasChanged(value, oldValue)) { // 修改属性且值发生变化触发 SET 类型的更新 trigger(target, key, TriggerOpTypes.SET, value, oldValue) } } return result } } -
trigger 触发更新详细流程
- trigger 函数根据修改类型执行相应的更新操作:
function trigger(target, key, type, newValue, oldValue) { // 1. 获取 target 对应的依赖映射 const depsMap = targetMap.get(target) if (!depsMap) return // 2. 创建要执行的 effects 集合 const effects = new Set() // 3. 添加 key 对应的 effects const add = (effectsToAdd) => { if (effectsToAdd) { effectsToAdd.forEach(effect => { effects.add(effect) }) } } // 4. 根据操作类型收集需要触发的 effects if (key !== void 0) { add(depsMap.get(key)) } // 5. 处理数组长度变化或新增索引的情况 if (type === TriggerOpTypes.ADD && isArray(target)) { add(depsMap.get('length')) } // 6. 执行所有收集到的 effects const run = (effect) => { if (effect.options.scheduler) { effect.options.scheduler(effect) // 使用调度器 } else { effect() // 直接执行 } } effects.forEach(run) } -
effect 副作用函数的执行机制
- effect 函数创建响应式副作用:
function effect(fn, options = {}) { // 1. 创建 ReactiveEffect 实例 const effect = createReactiveEffect(fn, options) // 2. 如果不是懒加载,立即执行一次 if (!options.lazy) { effect() } return effect } function createReactiveEffect(fn, options) { const effect = function reactiveEffect() { // 1. 清理 effect 与之前依赖的关联 cleanup(effect) // 2. 设置当前活跃的 effect activeEffect = effect // 3. 执行原始函数(期间会触发 track) return fn() } // 4. 附加属性 effect.deps = [] // 存储该 effect 依赖的所有 dep 集合 effect.options = options return effect }
这个完整的执行流程确保了 Vue3 响应式系统的高效运行,通过 Proxy 拦截、依赖收集和精准触发更新,实现了数据的自动响应。