vue2的方式
// 拦截每个key,从而可以侦测数据变化
function defineReactive(obj, key, val) {
Object.defineProperty(obj, key, {
get() {
return val
},
set(v) { val = v
update()
}
})
}
function update() { console.log(obj.foo);
}
const obj = {}
defineReactive(obj, 'foo', 'foo')
obj.foo = 'foo1'
vue3的方式
// 代理整个对象,从而侦测数据变化
function defineReactive(obj) {
return new Proxy(obj, {
get(target, key) {
return target[key]
},
set(target, key, val) {
target[key] = val
update()
}
})
}
function update() { console.log(obj.foo);
}
const obj = {}
const observed = defineReactive(obj)
observed.foo = 'foo1'
Vue2 vs Vue3
1、 对象响应化:vue2递归遍历每个 key(速度慢),使用 Object.defineproperty 方法定义 getter.setter
// 1.对象响应化:遍历每个key,定义getter、setter
function observe(obj) {
if (typeof obj !== 'object' || obj == null) { return
}
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
defineReactive(obj, key, obj[key])
}
}
function defineReactive(obj, key, val) {
observe(val)
Object.defineProperty(obj, key, {
get() {
return val
},
set(newVal) {
if (newVal !== val) {
observe(newVal) val = newVal
dep.notify()
}
}
})
}
2、数组响应化:采用函数拦截方式, 覆盖数组原型方法,额外增加通知逻辑
const originalProto = Array.prototype
const arrayProto = Object.create(originalProto)
['push', 'pop', 'shift', 'unshift', 'splice', 'reverse', 'sort'].forEach(
method => {
arrayProto[method] = function() {
originalProto[method].apply(this, arguments)
dep.notify()
}
})
所以在vue2 中
1、新增或删除属性无法监听,需要使用特殊api
Vue.set(obj, 'foo', 'bar')
Vue.delete(obj, 'foo')
2、检测数组变动,需要使用特殊api
// 修改数组某index的值
Vue.set(arr, index, newValue)
// 修改数组长度
arr.splice(newLength)
总结:vue2的响应式弊端
1、 对象响应化:递归遍历每个 key,使用 Object.defineproperty 方法定义 getter.setter
2、数组响应化:采用函数拦截方式,覆盖数组原型方法,额外增加通知逻辑
3、Map.Set.Class 等无法响应式,修改语法有限制
4、由此增加了多个不同的 api 操作