根据上一节,我们知道了使用defineProperty来监听数据的变化。大概知道响应式的基本原理。这一节我们可以通过另外一种方式来做,既使用ES Next的新特性-----Proxy做数据代理。去实现数据监听。
Proxy
Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。
语法:
const p = new Proxy(target, handler)
参数:
target
( 要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。)
handler
( 一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为。)
handler
handler 对象是一个容纳一批特定属性的占位符对象。它包含有 Proxy 的各个捕获器(trap)。
所有的捕捉器是可选的。如果没有定义某个捕捉器,那么就会保留源对象的默认行为。
捕捉器等更多详细 请查看链接 Proxy详细说明
首先我们先尝试使用Proxy来完成上一节代码的重构。
let data = {
name: 'fzs',
info: {
age: 18,
driving: 2,
familyTies: {
maritalStatus: '已婚'
},
hobby: ['乒乓球', '游泳']
},
}
const observe = data => {
if (!data || Object.prototype.toString.call(data) !== '[object Object]') {
return
}
Object.keys(data).forEach(key => {
let currentValue = data[key]
// 事实上, Proxy也可以对函数类型进行代理。这里只对承载数据类型的Object进行处理。大家了解即可
if (typeof currentValue === 'object') {
// 递归处理对象类型,通过proxy代理
observe(currentValue)
data[key] = new Proxy(currentValue, {
set: (target, property, value, reciver) => {
// setter中执行各种操作,存储等
console.log('这是赋值操作')
if (value === currentValue) {
console.log('不需要重新赋值处理')
return true
}
if (property !== 'length') {
console.log('数组只需要执行一次赋值操作,因为push方法会引起length的变化,触发两次set操作,我们只需要保留一次即可')
// 这里进行我们数组的赋值拦截的其他操作
}
return Reflect.set(target, property, value) //todo 这是赋值操作
},
})
} else {
Object.defineProperty(data, key, {
enumerable: false,
configurable: false,
get() {
console.log(`getting ${key} value now, getting value is:`, currentValue)
return currentValue
},
set(newValue) {
currentValue = newValue
console.log(`setting ${key} value now, setting value is`, currentValue)
}
})
}
})
}
observe(data)
// 对数组进行如下操作
if (data.info.hobby) {
data.info.hobby.push('打篮球')
}
观察输出我们发现程序已经监听到了深层数据的变动。
简单总结一下:
1.对于数据键值类型为基本类型的情况,我们可以继续使用
Object.defineProperty
2.对于键值为对象类型的情况,我们可以继续递归调用observe
方法,并通过Proxy
返回的新对象对data[key]
重新赋值,这个新值的getter
和setter
已经被添加了代理。
了解了Proxy
实现之后,我们对使用Proxy实现数据代理和使用Object.defineProperty
实现数据拦截进行对比,可以得出一下 结论。
-
Object.defineProperty
不能监听数组的变化,需要对数组方法进行重写。 -
Object.defineProperty
必须遍历对象的每个属性,且需要对嵌套结构进行深层遍历。 -
Proxy
的代理是针对整个对象的,而不是针对 对象 的某个属性。因此不像Object.defineProperty
必须遍历对象的每个属性,Proxy
只需要做一层代理就可以监听同级结构下的所有属性变化。当然对于深层结构,递归还是需要进行的。 -
Proxy
支持代理数组的变化。 -
Proxy
的第二个参数除了可以使用 set 和 get, 还可以使用13种拦截方法,比Object.defineProperty
更强大。 Proxy详细说明 - 使用
Proxy
时,性能将会被底层持续优化;而使用Object.defineProperty
时,性能已经不再是优化重点。