-
Notifications
You must be signed in to change notification settings - Fork 3.3k
第 51 题:Vue 的响应式原理中 Object.defineProperty 有什么缺陷?为什么在 Vue3.0 采用了 Proxy,抛弃了 Object.defineProperty? #90
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
push()
pop()
shift()
unshift()
splice()
sort()
reverse() 由于只针对了以上八种方法进行了hack处理,所以其他数组的属性也是检测不到的,还是具有一定的局限性。
而要取代它的Proxy有以下两个优点;
|
楼上讲的很细致啦!有一点我补充一下: Object.defineProperty本身有一定的监控到数组下标变化的能力: 关于 Vue 3.0 的其他信息我们可以参考 尤大大发布的 Vue 3.0 新特性预览PPT 另外补充一些其他资料给大家: |
感觉 Proxy 行为有些复杂,一个操作会引起很多中间操作出现,这部分有哪位盆友讲解一下或者指个路,介绍一下Vue是怎样处理这些干扰信息的吗? let a = [1];
let newA = new Proxy(a, {
get(target, p, receiver) {
console.log('get', p);
return Reflect.get(target, p, receiver);
},
set(target, p, value, receiver) {
console.log('set', p);
return Reflect.set(target, p, value, receiver);
}
});
newA.push(1);
console.log('===');
newA[2] = 2;
newA.length;
console.log('===');
newA.length = 100;
console.log('===');
newA.shift(); 输出 get push
get length
set 1
set length
===
set 2
get length
===
set length
===
get shift
get length
get 0
get 1
set 0
get 2
set 1
set length |
1, 数据的变化是通过getter/setter来追踪的。因为这种追踪方式,有些语法中,即便是数据发生了变化,vue也检查不到。比如 向Object添加属性/ 删除Object的属性。 2,检测数组的变化,因为只是拦截了unshift shift push pop splice sort reverse 这几个方法,所以像 list[0] = 4
list.length = 0 检测不到 |
之前正好写过个类似的东西,大概就是 Object.defineProperty 的监听只会根据第二个参数的 prop 来判断,甚至内存指针指向同一数据都不会监听,而且不会往下递归。 let a = {
b:{c1:1,c2:2},
}
Object.defineProperty(a, 'b', {
get: function() {
console.log(a.b);
}
})
// Maximum call stack size exceeded
// 循环调用,肯定是爆栈了 let a = {
b:{c1:1,c2:2},
}
let temp = a.b
Object.defineProperty(a, 'b', {
get: function() {
return temp
}
})
temp.c1 = 99
console.log(a.b, temp); // {c1: 99, c2: 2} {c1: 99, c2: 2}
// 两个对象指针还是指向同一个数据,但是存取描述符只会根据 obj 和 prop 来判断 let a = {
b:{c1:1,c2:2},
}
let temp = a.b
Object.defineProperty(a, 'b', {
get: function() {
return temp
},
set: function(newVal) {
console.log(newVal);
}
})
temp.c1 = 99 // 并没有调用 setter
console.log(a.b, temp); // {c1: 99, c2: 2} {c1: 99, c2: 2}
// 还是说明存取描述符只会根据 obj 和 prop 来判断 知道了存取描述符只会根据 obj 和 prop 来判断,那监听数据就可以这样写 void (function() {
function watch(obj, name) {
let value = obj[name]
Object.defineProperty(obj, name, {
get: function() {
return value
},
set: function(newVal) {
value = newVal
}
})
value ? (obj[name] = value) : '' // 拿到外面处理,不然会循环 setter
}
return (this.watch = watch)
})()
let a = { b: 11 }
watch(a, 'b')
console.log(a.b) // 11
a.b = 12
console.log(a.b) // 12 需要做什么处理,在 watch 里再加个回调函数就可以了。 在 es6 还提供了一种新的监控方式。 let a = {
b:{c1:1,c2:2},
}
let proxy = new Proxy(a, {
get: function(obj, prop) {
console.log(obj, prop); // {b:{c1:1,c2:2}} , 'b'
return obj[prop]
}
})
console.log(proxy.b.c1); // 1
let p2 = proxy.b
console.log(p2.c1); // 1 可以发现,proxy 的监控方式是往下递归的,不只 prop,prop 下面的对象也可以触发。 |
@zhl1232 proxy并不是递归的,你的代码中执行proxy.b.c1只触发了 proxy对象的get,但并没有触发proxy.b的get |
@julyL 确实,这块和 defineProperty 的表现是一致的,理解错误,感谢大佬 |
Object.defineProperty可以监听到数组下标的变化的 |
前面补充的挺详细的了,这里就补充一下数组哪方面不能监控
|
这里其实提到了很多,简单来说,就是 length 尽量不能去改写。
|
Object.defineProperty是通过遍历整个对象来劫持的,所以原本数组的监听也需要遍历才能劫持,但是数组劫持遇到类似let list = new Array(1000000)的情况,性能开销太大。所以vue源码才会重写数组的方法和监听length来进行劫持。 |
The text was updated successfully, but these errors were encountered: