在Vue2.X中,是利用Object.defineProperty()方法来侦测对象的变化。但这种方法有以下几种缺陷
1) 无法侦测删除属性或新增属性
2) 改变数组的length属性无法被侦测
3) 性能较差
而Vue3.0中则使用了es6的Proxy来进行变化侦测。Proxy相比于Object.defineProperty()方法来讲性能更优异。数组的变化也可以直接get()和set()方法,不需要像Vue2.X那样单独处理数组的变化侦测。Proxy称为代理,他为javascript提供了元编程的能力,是一种可以直接拦截并改变底层javascript引擎操作的包装器。
对es6不太了解的朋友推荐阅读阮一峰的es6标准入门
下面是一个创建代理的简单示例
const target = {name: 'foo'}
const proxy = new Proxy(target, {
//陷阱函数,读取target属性值时触发
//三个参数分别是目标对象,读取的属性名,proxy对象本身
get(target, property, receiver) {
console.log(`${property}属性已被读取`)
return target[property]
},
//改变属性值时触发
set(target, property, value, receiver) {
console.log(`${property}属性已被设置为${value}`)
target[property] = value
return value
},
//删除属性时触发
deleteProperty (target, property) {
console.log(`${property}属性已被删除`)
}
})
const name = proxy.name //name属性已被读取
proxy.name = 'bar' //name属性已被设置为bar
delete proxy.name //name属性已被删除
上面是使用Proxy侦测对象变化的简单示例,下面让我们来模拟实现一下Vue3.0的响应式系统
//判断某个变量是否是对象的辅助函数
function isObject (val) {
return val !== null && typeof val === 'object'
}
//创建响应式对象
function reactive (target) {
return createReactiveObject(target)
}
function createReactiveObject (target) {
//如果不是对象则直接返回
if (!isObject(target)) {
return target
}
const baseHandler = {
get (target, property, receiver) {
console.log('读取值')
const result = Reflect.get(target, property, receiver)
//递归地解析嵌套对象,解决多层对象嵌套问题
return isObject(result) ? reactive(result) : result
},
set (target, property, value, receiver) {
console.log('设置值')
const result = Reflect.set(target, property, value, receiver)
return result
},
deleteProperty (target, property) {
console.log('删除值')
const result = Reflect.deleteProperty(target, property)
return result
}
}
const observed = new Proxy(target, baseHandler)
return observed
}
const proxy = reactive({name: 'foo'})
proxy.name = 'bar' //设置值
console.log(proxy.name)//读取值,bar
看过之前的例子,上面的代码应该很容易理解。在创建响应式对象的reactive函数中,我们只需要对传入的target对象进行一次包装,返回包装后的Proxy对象就可以。之后我们就可以在Proxy对象的陷阱函数里面侦测变化。