在Vue3中,最重要也更为人所知的就是ES6的Proxy。
Proxy不仅消除了Vue2中现有的限制(比如对象新属性的增加、数组元素的直接修改不会触发响应式机制,这也是很多新手以为所谓的bug),而且有着更好的性能:
我们知道,在Vue2中对数据的侦听劫持是在组件初始化时去遍历递归一个对象,给其中的每一个属性用Object.defineProperty设置它的getter和setter,然后再把处理好的这些对象挂到组件实例的this上面,所以这种方式的数据侦听是在属性层面的,这也是为什么增加对象属性无法被监听的原因,同时这种初始化的操作对于CPU来说还是比较昂贵的一个操作。对于javascript而言,一个对象肯定越稳定越小性能就越好。
使用Proxy之后组件的初始化就不需要做这么费时的操作了,因为Proxy就是真正意义给一个对象包上一层代理从而去完成数据侦听劫持的操作,所以总的来说这个复杂度是直接少了一个数量级的。只要你对这个代理后的对象访问或修改里面的东西都能被这层代理所感知到,进而去动态地决定返回什么东西给你,并且也不再需要把选项的这些东西再重复挂到组件实例的this上面,因为你访问的时候是有这个信息知道你访问的东西是属于props还是data还是其他的,vue只需要根据这个信息去对应的数据结构里面拿出来就可以了,单就这一点而言你就可以感觉到组件的内存占用已经少了一半。
由于proxy是在对象层面上的代理,所以你在对象上新增属性是可以被代理到的。
另外Proxy还可以代理数组,所以就算你直接修改数组里面的元素也是可以被代理到的。
以上摘自《了不起的vue3》,下面上代码,自己实现vue的双向绑定,帮助理解其原理
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>vue双向绑定原理</title>
</head>
<body>
<div></div>
<input type="text">
<script>
// vue2.0 利用 Object.defineProperty 实现双向绑定
let box, input, data, obserData;
box = document.querySelector('div');
input = document.querySelector('input');
data = 'default';
obserData = {};
Object.defineProperty(obserData, 'data', {
get: () => {
return data;
},
set: (newData) => {
// 设置data时,更新视图并更新数据
input.value = newData;
box.innerHTML = newData;
data = newData;
}
})
input.addEventListener('keyup', function () {
obserData.data = this.value;
})
// 模拟生命周期
setTimeout(() => {
obserData.data = '更新';
}, 1000)
</script>
</body>
</html>
在vue3.0中
<script>
let box, input, data;
box = document.querySelector('div');
input = document.querySelector('input');
data = 'default';
// vue3.0利用es6的proxy实现双向绑定
const obserData = new Proxy({}, {
get: (obj, prop) => {
return obj[prop];
},
set: (obj, prop, value) => {
// 设置data时,更新视图并更新数据
if (prop === 'data') {
input.value = value;
box.innerHTML = value;
data = value;
}
}
})
input.addEventListener('keyup', function () {
obserData.data = this.value;
})
// 模拟生命周期
setTimeout(() => {
obserData.data = '更新';
}, 1000)
</script>
上面是为了大家方便理解proxy而延用了vue2的思想写的,实际上,data本身就是对象形式,proxy是直接对此进行代理的,如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>vue双向绑定原理</title>
</head>
<body>
<div></div>
<input type="text">
<script>
let box, input, data;
box = document.querySelector('div');
input = document.querySelector('input');
data = { data: 'default' };
// vue3.0利用es6的proxy实现双向绑定
const obserData = new Proxy(data, {
get: (obj, prop) => {
return obj[prop];
},
set: (obj, prop, value) => {
// 设置data时,更新视图并更新数据
if (prop === 'data') {
input.value = value;
box.innerHTML = value;
obj[prop] = value;
}
}
})
input.addEventListener('keyup', function () {
obserData.data = this.value;
})
// 模拟生命周期
setTimeout(() => {
obserData.data = '更新';
}, 1000)
</script>
</body>
</html>