您好,登录后才能下订单哦!
密码登录
登录注册
点击 登录注册 即表示同意《亿速云用户服务条款》
# 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进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。