为什么要用 Proxy API 代替 defineProperty API
1. Object.defineProperty
定义:Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
为什么能实现响应式
通过 defineProperty 的两个属性:get 及 set。
缺点
- 对一个对象进行 删除和添加属性 操作,无法劫持到。
- 无法对 数组 API 进行监听。
- 对对象深层的嵌套对象进行监听存在性能问题。
正因为 defineProperty 自身的缺陷,导致 Vue 2 在实现响应式过程中需要实现其他的方法辅助(如重写数组方法、增加额外 set、delete 方法)。
2. Proxy
Proxy 的监听是针对一个对象的,那么对这个对象的所有操作会进入监听操作,这就完全可以代理所有属性了:
function reactive(obj) {
if (typeof obj !== 'object' && obj == null) {
return obj
}
// Proxy 相当于在对象外层加拦截
const observed = new Proxy(obj, {
get(target, key, receiver) {
const res = Reflect.get(target, key, receiver)
console.log(`获取 ${key}:${res}`)
return res
},
set(target, key, value, receiver) {
const res = Reflect.set(target, key, value, receiver)
console.log(`设置 ${key}:${value}`)
return res
},
deleteProperty(target, key) {
const res = Reflect.deleteProperty(target, key)
console.log(`删除 ${key}:${res}`)
return res
}
})
return observed
}
再测试嵌套对象情况,这时发现就不那么 OK 了:
const state = reactive({
bar: { a: 1 }
})
// 设置嵌套对象属性
state.bar.a = 10 // no ok
如果要解决,需要在 get 之上再进行一层代理:
function reactive(obj) {
if (typeof obj !== 'object' && obj == null) {
return obj
}
// Proxy 相当于在对象外层加拦截
const observed = new Proxy(obj, {
get(target, key, receiver) {
const res = Reflect.get(target, key, receiver)
console.log(`获取 ${key}:${res}`)
return isObject(res) ? reactive(res) : res
}
})
return observed
}
Proxy 的优势
- Proxy 可以直接监听数组的变化(
push、shift、splice等)。 - Proxy 有多达 13 种拦截方法,不限于
apply、ownKeys、deleteProperty、has等,这是Object.defineProperty不具备的。 - Proxy 天然支持 新增 / 删除属性 监听,不需要额外
set/delete钩子。 - Proxy 直接代理整个对象,不需要深度遍历 初始化的所有属性。
来源整理自:vue3js.cn 面试官系列



