版本迭代
1、Vue3之前是使用Object.defineProperty实现的,缺点有以下三点
- 深度监听需要一次性递归 (遍历每个对象的每个属性,如果对象嵌套很深的话,需要使用递归调用。)
- 无法监听新增属性/删除属性(Vue.set Vue.delete,未在 data 中定义的属性会报 undefined)
- 无法原生监听数组,需要特殊处理
// 新增
Vue.set(obj, newkey, newvalue)
vm.$set(obj, newkey, newvalue)
obj = Object.assign({}, obj, {newkey1: newvalue1, newkey2: newvalue2})
// 删除
Vue.delete(obj, key)
vm.$delete(obj, key)
// 数组监听方案
// 修改值
Vue.set(arr, index, newvalue)
vm.$set(arr, index, newvalue)
arr.splice(index, 1, newvalue)
// 修改长度
arr.splice(newLen)
2、Vue3使用Proxy实现
- Proxy可以理解成,在目标对象之前架设一层 "拦截",当外界对该对象访问的时候,都必须经过这层拦截,而Proxy就充当了这种机制,类似于代理的含义,它可以对外界访问对象之前进行过滤和改写该对象。
// 被代理之后返回的对象 = new Proxy(被代理对象,要代理对象的操作)
const obj = new Proxy(target, handler)
- handler中常用的对象方法如下:
get(target, propKey, receiver)
set(target, propKey, value, receiver)
has(target, propKey)
construct(target, args):
apply(target, object, args)
Proxy实现监听
// 创建响应式
function reactive(target {}) {
if (typeof target !== 'object' || typeof target == null) {
return target // 不是对象或数组直接返回
}
// 代理配置
const proxyConf = {
get(targe, key, receiver) {
// 只处理本身(非原型)的属性
const ownKeys = Reflect.ownKeys(target)
if(ownKeys.include(key)) {
console.log('get', key) // 监听
}
const result = Reflect.get(target, key, receiver) // 返回不做处理
return reactive(result) // 递归调用,这里所做的优化是只在调用到对象深层次的属性时才会触发递归
}
set(target, key, val, receiver) {
// 重复的数组,不处理
if(val === target[key]) {
return true;
}
const ownKeys = Reflect.ownKeys(target)
if(ownKeys.include(key)) {
console.log('set 已有的属性', key) // 监听
} else {
console.log('新增的属性', key)
}
const result = Reflect.set(target, key, val, receiver)
console.log('set', key, val)
return result // 是否设置成功
}
deleteProperty(target, key) {
const result = Reflect.deleteProperty(target, key)
console.log('deleteProperty', key)
return result
}
})
// 生成代理 对象
const observed = new Proxy(target, proxyConf)
return observed;
}
// 测试数据
const data = {
name: 'zhangsan',
age: 20,
info: {
city: 'beijing',
a: {
b: {
c: {
d: e
}
}
}
}
}
object.definePropety 的深度监听是一次性就全部监听的,而 proxy 的深度监听是在 get 的时候才去递归的,是一个惰性的,很慢的过程,这就是 proxy 性能的优化
Proxy 优缺点总结
规避了 Object.definedProperty的问题
Proxy 无法兼容所有浏览器,无法进行polyfill
通过proxy直接代理整个对象来实现的,而不是像Object.defineProperty针对某个属性。所以,只需做一层代理就可以监听同级结构下的所有属性变化,包括新增属性和删除属性