八股文背的滚瓜烂熟不如看一看实现的原理,下面我以简易的版本为你介绍这两种方式的主要差异
// 判断是否为一个对象
function isObject(e) {
return typeof e === 'object' && e !== null
}
// vue2 响应式原理
var data = {
a: 1,
b: 2,
c: 3,
d: {
e: 5,
f: 6
}
}
function observe(data) {
for (const key in data) {
let val = data[key]
Object.defineProperty(data, key, {
get() {
console.log('defineProperty get');
if (isObject(val)) observe(val)
return val
},
set(value) {
console.log('defineProperty set');
val = value
}
})
}
}
observe(data)
// vue3响应式原理
var data2 = {
a: 1,
b: 2,
c: 3,
d: {
a: 1,
b: 2
}
}
function refImpl(data) {
const proxyInstance = new Proxy(data, {
get(target, key, receiver) {
console.log('proxy get');
if (isObject(target[key])) return refImpl(target[key])
return Reflect.get(target, key, receiver)
},
set(target, key, value, receiver) {
console.log('proxy set');
return Reflect.set(target, key, value, receiver)
}
})
return proxyInstance
}
const proxy = refImpl(data2)
window.proxy = proxy
从代码层面上看,vue2使用了Object.defineProperty,直接通过递归遍历每一个data属性实现了对象的劫持
缺点:
- 如果data属性嵌套太深容易出现内存溢出的问题
- 数据在created阶段已经递归遍历完成,后期增加的属性不会被劫持到,vue官网推荐使用$set
- 不能监听数组的变化
优点:兼容性比较好,低版本ie也支持
vue3采用了proxy实现了对象的代理,不用递归,直接就能监听整个对象
优点:
- 不用递归即可监听整个对象/数组
缺点:
- 不兼容低版本ie