参考文档
https://www.zhihu.com/question/460133198
https://cn.vuejs.org/guide/extras/reactivity-in-depth.html
https://juejin.cn/post/7103764386220769311
Reflect API 一般搭配 Proxy API 一起使用。什么是 Proxy API 呢?
先回顾下 vue 的数据响应性是如何实现的。
Vue2 用逐个属性 Object.defineProperty()改 get 和 set 方法来实现,对于某些情况数据绑定不生效,要强制刷新。比如直接改变数组下标(defineProperty能监听,但因性能问题放弃。数组部分 api 比如 push 通过重写实现监听);无法监听属性的增加和删除。层级深时还会有性能问题。
到了 Vue3 改为用 Proxy、 Reflect API 实现。相比defineProperty不需要遍历所有属性要改写 get 和 set 方法,提高效率。
let validator = {set: function (obj, prop, value, receiver) {return Reflect.set(obj, prop, value, receiver)},
};let person = new Proxy({}, validator);
对于 Vue2 之前的问题也能解决:
- 无法监听删除属性
- 无法监听新增属性,直接改变数组下标
看到上面的代码,你会好奇,为啥 set 要用 Reflect.set
,不直接object[prop]=value
? 还要最后的 receiver 参数有什么用?
看文档,receiver 是 this 的指向,如果我们直接object[prop]=value
,会有什么问题呢?
const a = {firstName:'Z', secondName: 1, get name () {return this.firstName+this.secondName}
}
const proxy = new Proxy(a, {get(obj, prop, receiver) {return obj[prop]},set(obj, prop,value) {obj[prop] = valuereturn true}
})
const b = {secondName:2
}
Object.setPrototypeOf(b, proxy);
console.log(b.name) // Z1
const a = {firstName:'Z', secondName: 1, get name () {return this.firstName+this.secondName}
}
const proxy = new Proxy(a, {get(obj, prop, receiver) {return Reflect.get(obj, prop, receiver)},set(obj, prop,value ,receiver) {return Reflect.set(obj, prop,value, receiver)}
})
const b = {secondName:2
}
Object.setPrototypeOf(b, proxy);
console.log(b.name) // Z2
文档里说 receiver是调用时 this 的指向,第一个参数 target 是目标对象,在上面的例子里,因为 b 没有 name 属性,所以沿着原型链去找,在 a 找到了,此时 get 方法里,obj 就是 a,recevier 仍是 b,我们按照预期应该是用 b 的 secondName 才对。
同时,b 是没有 firstName 的,看结果最后也从 a 里取到 firstName 的值了。
Reflect API 除了能保证 this 的指向正确,还帮助我们在进行 Proxy 编程时代码更清晰。
这里只是 get 和 set 的例子, 可以直接return obj[prop]
或者obj[prop]=value
,但 Proxy 还有很多其他的 API,如果这些 API都 要重新实现一次原本的行为比较麻烦,也不清晰。直接调用 Reflect.xxx这样就方便很多。