Vue3响应式原理深度解析:揭秘Proxy的魔力

Vue3响应式原理深度解析:揭秘Proxy的魔力 Vue3响应式原理深度解析揭秘Proxy的魔力前言大家好我是前端老炮儿今天咱们来聊聊Vue3响应式原理。你以为Vue3的响应式就是简单的ref和reactive那你可太天真了其背后的Proxy机制才是真正的黑科技。为什么需要响应式数据驱动视图的挑战数据变化 → 自动更新视图 → 需要追踪依赖Vue2 vs Vue3Vue2: 使用Object.definePropertyVue3: 使用Proxy优势: 性能更好、功能更强大响应式核心概念1. 响应式数据// Vue3响应式系统的核心组件 const target { count: 0 } const proxy 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 } })2. 依赖收集// 依赖收集器 let activeEffect null function effect(fn) { activeEffect fn fn() // 执行一次触发依赖收集 activeEffect null } function track(target, key) { if (activeEffect) { // 获取target的依赖映射 let depsMap targetMap.get(target) if (!depsMap) { depsMap new Map() targetMap.set(target, depsMap) } // 获取key的依赖集合 let deps depsMap.get(key) if (!deps) { deps new Set() depsMap.set(key, deps) } // 添加当前effect到依赖集合 deps.add(activeEffect) } }3. 触发更新// 触发更新 const targetMap new WeakMap() function trigger(target, key) { const depsMap targetMap.get(target) if (!depsMap) return const deps depsMap.get(key) if (deps) { // 执行所有依赖的effect deps.forEach(effect effect()) } }ref的实现原理ref的本质class RefImpl { constructor(value) { this._value value // 将值转换为响应式 this._rawValue toRaw(value) this.dep createDep() } get value() { // 依赖收集 trackRefValue(this) return this._value } set value(newValue) { newValue toRaw(newValue) if (hasChanged(newValue, this._rawValue)) { this._rawValue newValue // 更新值如果是对象转为响应式 this._value toReactive(newValue) // 触发更新 triggerRefValue(this) } } } // 创建ref function ref(value) { return createRef(value, false) } function createRef(value, shallow) { return new RefImpl(value, shallow) }ref的解包// 在reactive中自动解包 const count ref(0) const state reactive({ count }) console.log(state.count) // 0自动解包 // 修改会触发响应 state.count console.log(count.value) // 1reactive的实现原理reactive的本质function reactive(target) { // 只能对对象类型创建响应式 if (target typeof target object) { return createReactiveObject( target, false, mutableHandlers, mutableCollectionHandlers ) } return target } function createReactiveObject(target, isReadonly, baseHandlers, collectionHandlers) { // 如果已经是响应式直接返回 if (target.__v_isReactive) { return target } // 创建Proxy const proxy new Proxy( target, targetType TargetType.COLLECTION ? collectionHandlers : baseHandlers ) // 标记为响应式 def(target, __v_isReactive, true) return proxy }Proxy处理器// mutableHandlers const mutableHandlers { get(target, key, receiver) { // 特殊处理 if (key __v_isReactive) return true // 依赖收集 track(target, TrackOpTypes.GET, 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 (hasChanged(value, oldValue)) { trigger(target, TriggerOpTypes.SET, key, value, oldValue) } return result }, deleteProperty(target, key) { const hadKey hasOwn(target, key) const result Reflect.deleteProperty(target, key) if (hadKey) { trigger(target, TriggerOpTypes.DELETE, key) } return result } }computed的实现原理computed的本质class ComputedRefImpl { constructor(getter, setter, isReadonly) { this._getter getter this._setter setter this._isReadonly isReadonly // 创建effect标记为computed this.effect effect(getter, { lazy: true, scheduler: () { // 触发更新 triggerRefValue(this) } }) this.dep createDep() } get value() { // 依赖收集 trackRefValue(this) // 如果是脏的重新计算 if (this._dirty) { this._value this.effect() this._dirty false } return this._value } set value(newValue) { if (!this._isReadonly) { this._setter(newValue) } } } function computed(getterOrOptions) { let getter let setter if (typeof getterOrOptions function) { getter getterOrOptions setter NOOP } else { getter getterOrOptions.get setter getterOrOptions.set } return new ComputedRefImpl(getter, setter, !setter) }computed的缓存机制// computed会缓存结果 const count ref(1) const doubled computed(() { console.log(computing...) return count.value * 2 }) console.log(doubled.value) // 输出: computing... 2 console.log(doubled.value) // 输出: 2缓存没有重新计算 count.value 2 console.log(doubled.value) // 输出: computing... 4依赖变化重新计算watch和watchEffect的实现原理watch的实现function watch(source, cb, options {}) { return doWatch(source, cb, options) } function doWatch(source, cb, options) { const { immediate, deep } options // 创建effect const effect new ReactiveEffect(() { if (isMultiSource) { // 多源监听 const values getMultiSourceValues(sources) if (deep) { traverse(sources) } return values } else { // 单源监听 const value getter() if (deep) { traverse(value) } return value } }) // 调度器 effect.scheduler () { const newValue effect.run() if (deep || forceTrigger || hasChanged(newValue, oldValue)) { cb(newValue, oldValue) oldValue newValue } } // 立即执行 if (immediate) { cb(newValue, undefined) } // 运行effect建立依赖 return effect.run() }watchEffect的实现function watchEffect(effect, options {}) { return doWatch(effect, null, options) }响应式的边界情况数组的处理// Proxy可以直接拦截数组操作 const arr reactive([1, 2, 3]) arr.push(4) // 会触发更新 arr[0] 10 // 会触发更新 arr.length 0 // 会触发更新新增属性的处理// Vue3可以直接添加新属性 const state reactive({ name: 张三 }) state.age 25 // 会触发更新Vue2不行深层对象的处理// 深层对象自动转为响应式 const state reactive({ user: { name: 张三, address: { city: 北京 } } }) state.user.address.city 上海 // 会触发更新性能优化1. 避免不必要的依赖收集// shallowRef只追踪顶层 const shallow shallowRef({ name: 张三 }) // 修改嵌套属性不会触发更新 shallow.value.name 李四 // 不会触发 // 需要手动触发 triggerRef(shallow)2. 避免不必要的触发// 相同值不会触发更新 const count ref(0) watch(count, (newVal, oldVal) { console.log(changed) }) count.value 0 // 不会触发值相同 count.value 1 // 会触发3. 使用readonly// readonly创建只读代理 const state reactive({ count: 0 }) const readOnlyState readonly(state) readOnlyState.count // 警告不会改变值实战手动实现响应式// 简化版响应式系统 class SimpleReactive { constructor() { this.targetMap new WeakMap() this.activeEffect null } track(target, key) { if (this.activeEffect) { let depsMap this.targetMap.get(target) if (!depsMap) { depsMap new Map() this.targetMap.set(target, depsMap) } let deps depsMap.get(key) if (!deps) { deps new Set() depsMap.set(key, deps) } deps.add(this.activeEffect) } } trigger(target, key) { const depsMap this.targetMap.get(target) if (!depsMap) return const deps depsMap.get(key) if (deps) { deps.forEach(effect effect()) } } reactive(target) { return new Proxy(target, { get: (target, key) { this.track(target, key) const result Reflect.get(target, key) if (typeof result object) { return this.reactive(result) } return result }, set: (target, key, value) { const result Reflect.set(target, key, value) this.trigger(target, key) return result } }) } effect(fn) { this.activeEffect fn fn() this.activeEffect null } } // 使用 const reactiveSystem new SimpleReactive() const state reactiveSystem.reactive({ count: 0 }) reactiveSystem.effect(() { console.log(Count changed:, state.count) }) state.count // 输出: Count changed: 1 state.count // 输出: Count changed: 2常见问题与解决方案Q1: 为什么ref需要.value原因JavaScript基本类型number、string等是值传递需要一个包装对象来实现响应式解决方案使用ref的.value属性在reactive中会自动解包Q2: 为什么修改数组索引不生效原因可能使用了shallowRef数组长度过大解决方案使用reactive或ref使用splice或push等方法Q3: 为什么computed不更新原因依赖没有正确追踪返回值没有变化解决方案确保依赖是响应式的检查返回值是否真的变化总结Vue3响应式原理核心要点Proxy代理使用Proxy拦截对象操作依赖收集track函数收集effect触发更新trigger函数执行effectref包装基本类型需要.value访问reactive直接代理对象自动处理嵌套computed懒计算缓存机制希望今天的分享能帮助你深入理解Vue3的响应式原理如果你有任何问题或建议欢迎在评论区留言关注我每天分享前端干货让我们一起成长