先简单解释下ES6中Proxy的语法, 然后从vue2的defineProperty的缺点去理解为什么使用Proxy 最后展示二者的区别
使用new Proxy的语法来创建一个拦截器,其中:
target 是指要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理handler 是指一个通常以函数作为属性的对象,用来定制拦截行为后续触发拦截时都需要使用myProxy(如上例)实例才能触发拦截,而不是直接使用target举例:
根据提示错误信息(get) // 状态码提示信息 const errorMessage = { 400: '错误请求', 401: '系统未授权,请重新登录', 403: '拒绝访问', 404: '请求失败,未找到该资源' } const proxy = new Proxy(errorMessage, { get(target,key) { const value = target[key] return value || '系统异常,请联系管理员' } }) // 输出 错误请求 console.log(proxy[400]) // 输出 系统异常,请联系管理员 console.log(proxy[500]) 校验类型(set): const target = { _id: 24, name: 'vuejs' } // 校验器 const validators = { name(val) { return typeof val === 'string'; }, _id(val) { return typeof val === 'number' && val > 1024; } } const createValidator = (target, validator) => { return new Proxy(target, { _validator: validator, set(target, propkey, value, proxy){ let validator = this._validator[propkey](value) // 根据校验器进行拦截 if(validator){ return Reflect.set(target, propkey, value, proxy) }else { throw Error(`Cannot set ${propkey} to ${value}. Invalid type.`) } } }) } const proxy = createValidator(target, validators) proxy.name = 'vue-js.com' // vue-js.com proxy.name = 10086 // Uncaught Error: Cannot set name to 10086. Invalid type. proxy._id = 1025 // 1025 proxy._id = 22 // Uncaught Error: Cannot set _id to 22. Invalid type原因如下:
Object.defineProperty 无法检测到对象属性的添加和删除 ,是因为这个方法只能监听已存在的属性。如果要解决可以使用Vue 提供的全局$set ,其本质也是给新增的属性手动 observer。数组的索引直接设置一个数组项无法检测,并不是defineProperty的锅,而是尤大在设计上对性能的权衡虽然说索引变更不是 defineProperty的锅,但新增索引的确是 defineProperty做不到的,所以就有了vue对数组的重写方法,还是跟$set一样,手动 observer。附:vue对数组的重写方法是指: 重新定义数组的push,pop,shift,unshift,splice,sort,reverse方法,调用以上方法时key的订阅者列表会通知订阅者们“值已改变”。如果调用的是push,unshift,splice方法,递归处理新增加的项。