第 51 题:Vue 的响应式原理中 Object.defineProperty 有什么缺陷?为什么在 Vue3.0 采用了 Proxy,抛弃了 Object.defineProperty?
Object.defineProperty 无法监控到数组下标的变化,导致通过数组下标添加元素,不能实时响应;
Object.defineProperty 只能劫持对象的属性,从而需要对每个对象,每个属性进行遍历,如果,属性值是对象,还需要深度遍历。Proxy 可以劫持整个对象,并返回一个新的对象。
Proxy 不仅可以代理对象,还可以代理数组。还可以代理动态增加的属性。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(11)
这里其实提到了很多,简单来说,就是 length 尽量不能去改写。
Object.defineProperty是通过遍历整个对象来劫持的,所以原本数组的监听也需要遍历才能劫持,但是数组劫持遇到类似let list = new Array(1000000)的情况,性能开销太大。所以vue源码才会重写数组的方法和监听length来进行劫持。
另外vue3 proxy用的是懒劫持,不是一上来就递归遍历整个对象,只是兼容性方面差点。
前面补充的挺详细的了,这里就补充一下数组哪方面不能监控
例如,
arr = [1],直接更改arr[10] = 20
,这样就监听不到了,因为length
在规范不允许重写,而arr[0]
直接更改是可以监听到的。这也是为什么vue重写这些方法的原因
Object.defineProperty可以监听到数组下标的变化的
只是常规的使用数组时,很少会用下标去操作,而使用api更符合我们的编码习惯
@julyL 确实,这块和 defineProperty 的表现是一致的,理解错误,感谢大佬
@zhl1232 proxy并不是递归的,你的代码中执行proxy.b.c1只触发了 proxy对象的get,但并没有触发proxy.b的get
之前正好写过个类似的东西,大概就是 Object.defineProperty 的监听只会根据第二个参数的 prop 来判断,甚至内存指针指向同一数据都不会监听,而且不会往下递归。
知道了存取描述符只会根据 obj 和 prop 来判断,那监听数据就可以这样写
需要做什么处理,在 watch 里再加个回调函数就可以了。
在 es6 还提供了一种新的监控方式。
Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,Proxy 可以理解成,在目标对象之前架设一层“拦截”。
可以发现,proxy 的监控方式是往下递归的,不只 prop,prop 下面的对象也可以触发。
而且因为是需要要 Proxy 对象来触发,所以拷贝 Proxy 对象也没影响。
1, 数据的变化是通过getter/setter来追踪的。因为这种追踪方式,有些语法中,即便是数据发生了变化,vue也检查不到。比如 向Object添加属性/ 删除Object的属性。
2,检测数组的变化,因为只是拦截了unshift shift push pop splice sort reverse 这几个方法,所以像
检测不到
感觉 Proxy 行为有些复杂,一个操作会引起很多中间操作出现,这部分有哪位盆友讲解一下或者指个路,介绍一下Vue是怎样处理这些干扰信息的吗?
输出
楼上讲的很细致啦!有一点我补充一下:
Object.defineProperty本身有一定的监控到数组下标变化的能力:
Object.defineProperty本身是可以监控到数组下标的变化的,但是在 Vue 中,从性能/体验的性价比考虑,尤大大就弃用了这个特性。具体我们可以参考 《记一次思否问答的问题思考:Vue为什么不能检测数组变动》这篇文章,文章底部配图中有尤大大的严肃回复截图; 下方的讨论区也很值得大家下去看一看,有对于 for / forEach / for .. in .. 几个循环方式的讨论。
关于 Vue 3.0 的其他信息我们可以参考 尤大大发布的 Vue 3.0 新特性预览PPT
另外补充一些其他资料给大家:
由于只针对了以上八种方法进行了hack处理,所以其他数组的属性也是检测不到的,还是具有一定的局限性。
而要取代它的Proxy有以下两个优点;
摒弃Object.defineProperty,基于 Proxy 的观察者机制探索