通过 Proxy 实现数据双向绑定
前言
用过 Vue 的人都知道 Vue 有双向绑定的功能,Vue 2 是通过 Object.defineProperty 实现的双向绑定,但是到 Vue3 中,便使用的 Proxy 进行双向绑定。今天就记录一下如何通过 Proxy 实现数据双向绑定。
defineProperty 缺陷
defineProperty 的作用是在一个对象上定义属性或者修改已有的属性。defineProperty 与 Proxy 相比,其缺点在于:
- 无法监听数组的变化;
- 只能劫持对象的属性,无法劫持一个完整的对象。
defineProperty 语法为:Object.defineProperty(obj, prop, desc)
第一个参数表示源对象;第二个参数表示对象的属性,第三个参数表示对该属性的描述。
let obj= {}
Object.defineProperty(obj, 'text', {
value: '我是一段文本',
writable: true, // 是否可以改变,
configurable:false,//是否可配置
enumerable:false //是否可枚举
})
defineProperty 双向绑定
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<input type="text" id="input">
<div id="text"></div>
</body>
<script>
var input = document.getElementById("input");
var text = document.getElementById("text");
var obj = {};
Object.defineProperty(obj,'text',{
get:function () {
console.log('get');
},
set:function (val) {
console.log(val);
input.value = val;
text.innerText = val;
}
})
input.addEventListener('keyup',function (e) {
obj.text = e.target.value;
})
</script>
</html>
Proxy 语法
Proxy 是代理的意思,是 ES6 的新语法,与 defineProperty 类似,都是对对象进行一层拦截。但 Proxy 修复了 defineProperty 的问题。可以劫持完整的对象。以下为基础用法。
var obj = {};
var obj1 = new Proxy(obj,{
get:function (target,key,receiver) {
console.log(target);
return Reflect.get(target,key,receiver);
},
set:function (target,key,val,receiver) {
console.log(target);
return Reflect.set(target,key,val,receiver);
},
has:function(target, key){
return Reflect.has(target, key);
}
})
以上是通过 Proxy 创建了一个具有拦截功能的 obj 对象。其中 Reflect 也是 ES6 的新语法,为操作对象而提供的 API, 起作用主要有:
- 将 Object 对象的一些明显属于语言内部的方法放到Reflect对象上;
- 修改 Object 方法返回的结果,使其变得更合理。如:Object.defineProperty(obj, name, desc)在无法定义属性的时候会报错,而Reflect.defineProperty(obj, name, desc)则会返回false;
- 让 Object 的操作都变成函数行为。如 Object 的命令式:name in obj和delete obj[name] 则与 Reflect.has(obj, name)、Reflect.deleteProperty(obj, name)相等;
- Reflect 对象的方法与 Proxy 对象的方法一一对应,只要 Proxy 对象上有的方法在 Reflect 上也能找到。
Proxy 双向绑定
利用 Proxy 的拦截功能,可以在对象的 set 方法上将值同时赋值给 input 和对应的 div。然后在 keyup 事件内给该对象的属性进行赋值。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<input type="text" id="input">
<div id="text"></div>
</body>
<script>
var input = document.getElementById("input");
var text = document.getElementById("text");
var obj = {};
var obj1 = new Proxy(obj,{
get:function (target,key,receiver) {
return Reflect.get(target,key,receiver);
},
set:function (target,key,val,receiver) {
if(key === 'text '){
input.value = val;
text.innerText = val;
}
return Reflect.set(target,key,val,receiver);
}
})
input.addEventListener('keyup',function (e) {
obj1.text = e.target.value;
})
</script>
</html>