# Vue2和Vue3数据响应式原理分析及如何实现 ## 目录 1. [响应式系统概述](#响应式系统概述) 2. [Vue2响应式原理](#vue2响应式原理) - [Object.defineProperty实现](#objectdefineproperty实现) - [数组处理特殊情况](#数组处理特殊情况) - [存在的问题](#存在的问题) 3. [Vue3响应式原理](#vue3响应式原理) - [Proxy基础实现](#proxy基础实现) - [Reflect的作用](#reflect的作用) - [性能优化](#性能优化) 4. [手写实现对比](#手写实现对比) - [Vue2风格实现](#vue2风格实现) - [Vue3风格实现](#vue3风格实现) 5. [升级变化总结](#升级变化总结) 6. [最佳实践建议](#最佳实践建议) ## 响应式系统概述 响应式编程是Vue的核心机制,其本质是建立**数据与依赖的自动关联**。当数据变化时,所有依赖该数据的相关操作(如DOM更新、计算属性等)都能自动执行。 ```javascript // 理想中的响应式行为 const data = { count: 0 } watchEffect(() => { console.log(`Count is: ${data.count}`) }) data.count++ // 应自动触发日志输出
Vue2通过Object.defineProperty
对数据对象进行递归劫持:
function defineReactive(obj, key) { let value = obj[key] const dep = new Dep() // 依赖收集器 Object.defineProperty(obj, key, { get() { if (Dep.target) { dep.depend() // 收集依赖 } return value }, set(newVal) { if (newVal === value) return value = newVal dep.notify() // 触发更新 } }) } // 递归处理整个对象 function observe(obj) { if (typeof obj !== 'object' || obj === null) return new Observer(obj) } class Observer { constructor(value) { if (Array.isArray(value)) { // 数组特殊处理 } else { this.walk(value) } } walk(obj) { Object.keys(obj).forEach(key => { defineReactive(obj, key) }) } }
由于Object.defineProperty
对数组无效,Vue2采用了拦截器模式:
const arrayProto = Array.prototype const arrayMethods = Object.create(arrayProto) ['push', 'pop', 'shift', 'unshift'].forEach(method => { const original = arrayProto[method] def(arrayMethods, method, function mutator(...args) { const result = original.apply(this, args) const ob = this.__ob__ ob.dep.notify() // 手动触发更新 return result }) })
Vue.set
Vue3采用ES6的Proxy实现响应式:
function reactive(obj) { return new Proxy(obj, { 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 } }) } // 依赖收集与触发 const targetMap = new WeakMap() function track(target, key) { if (!activeEffect) return let depsMap = targetMap.get(target) if (!depsMap) { targetMap.set(target, (depsMap = new Map())) } let dep = depsMap.get(key) if (!dep) { depsMap.set(key, (dep = new Set())) } dep.add(activeEffect) } function trigger(target, key) { const depsMap = targetMap.get(target) if (!depsMap) return const effects = depsMap.get(key) effects && effects.forEach(effect => effect()) }
this
绑定Reflect.set
返回布尔值)function reactive(obj) { // 已有代理直接返回 if (proxyMap.has(obj)) { return proxyMap.get(obj) } const proxy = new Proxy(obj, { get(target, key, receiver) { if (key === '__isReactive') return true const res = Reflect.get(target, key, receiver) track(target, key) return isObject(res) ? reactive(res) : res // 惰性代理 } // ...其他trap }) proxyMap.set(obj, proxy) return proxy }
class Dep { constructor() { this.subs = new Set() } depend() { if (Dep.target) this.subs.add(Dep.target) } notify() { this.subs.forEach(sub => sub()) } } function defineReactive(obj, key) { const dep = new Dep() let value = obj[key] Object.defineProperty(obj, key, { get() { dep.depend() return value }, set(newVal) { if (newVal === value) return value = newVal dep.notify() } }) }
const effectStack = [] function effect(fn) { const e = createReactiveEffect(fn) e() return e } function createReactiveEffect(fn) { const effect = function() { try { effectStack.push(effect) return fn() } finally { effectStack.pop() } } return effect } const proxyMap = new WeakMap() function reactive(target) { const existingProxy = proxyMap.get(target) if (existingProxy) return existingProxy const proxy = new Proxy(target, { get(target, key, receiver) { track(target, key) const res = Reflect.get(target, key, receiver) return typeof res === 'object' ? reactive(res) : res }, 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 } }) proxyMap.set(target, proxy) return proxy }
特性 | Vue2 | Vue3 |
---|---|---|
核心API | Object.defineProperty | Proxy |
数组处理 | 方法重写 | 原生支持 |
新增属性 | 需要Vue.set | 直接支持 |
数据类型支持 | 有限 | 全面 |
性能 | 初始化递归消耗大 | 按需代理 |
代码量 | 约1,000行核心代码 | 约600行核心代码 |
Vue2项目:
Vue.set
Vue3项目:
reactive
和ref
readonly
markRaw
跳过代理性能优化:
// 避免不必要的响应式 const staticData = markRaw({ largeList: [...], // 不会被代理 config: {...} })
组合式开发:
export function useCounter() { const count = ref(0) const double = computed(() => count.value * 2) function increment() { count.value++ } return { count, double, increment } }
响应式系统的演进体现了前端技术的快速发展,理解其原理有助于我们编写更高效的Vue应用。 “`
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。