一、通过 Object.defineProperty 来实现
- 在html里面增加如下两个节点:
<input type="text" id="demo">
<div id="test"></div>
- 在scritpt里面写如下脚本:
<script>
const obj={}
//用于设置obj的属性值为name
Object.defineProperty(obj,'name',{
//获取到设置set的值
//读取到obj.name属性值的更改
set:function(value){
document.getElementById('test').innerHTML=value
}
})
console.log(obj.name)
//获取到文本框中的值并赋给obj.name
document.getElementById('demo').oninput=function (e) {
obj.name=e.target.value
}
</script>
以上就是一个简单的双向数据绑定。
二、用Proxy实现
简单的Proxy双向绑定原理,其实我个人的理解就是通过拦截对象在读/写时的操作就能实现双向绑定了:
class Observable {
constructor(data) {
this.data = data;
this.proxy = new Proxy(this.data, {
get: (target, property) => {
console.log(`获取属性: ${property}`);
return target[property];
},
set: (target, property, value) => {
console.log(`设置属性: ${property}`);
target[property] = value;
// 这里可以添加更新视图的逻辑
}
});
}
getData() {
return this.proxy;
}
}
// 使用示例
const observable = new Observable({ name: '张三', age: 30 });
const data = observable.getData();
data.name; // 控制台打印: 获取属性(读属性): name
data.age = 31; // 控制台打印: 设置属性(写属性): age
proxy用于实现数据劫持,从而实现双向数据绑定。可以通过监听proxy对象的变化,将变化反应到页面上,实现的效果是:当input输入框发生变化,能将变动内容实时的更新到页面上,下面是简单的实现:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
<title>简单的proxy双向数据绑定</title>
<style>
</style>
</head>
<body>
<div class="input-card">
<input type="text" id="name">
<p id="showInput">{{proxyObj.name}}</p>
</div>
<script>
const regexp = /^{{.*}}$/
const show = document.getElementById('showInput')
let proxyObj = new Proxy({ name: '' }, {
get(target, propKey, receiver) {
return Reflect.get(target, propKey, receiver);
},
set(target, propKey, value, receiver) {
// 此处进行视图更新
show.innerHTML = value;
return Reflect.set(target, propKey, value, receiver);
}
})
window.onload = function() {
if (regexp.test(show.innerHTML)) {
show.innerHTML = '';
}
const n = document.getElementById('name')
n.addEventListener('input', (event) => {
// 进行此步操作会触发proxy中的set方法,是在进行name属性的写入操作
proxyObj.name = event.target.value;
})
}
</script>
</body>
</html>
完整代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>双向数据绑定.v1</title>
</head>
<body>
显示title:<h1 id="h1title">{{data.title}}</h1>
输入title:<input id="input" type="text" v-model="data.title" />
</body>
<script>
var regexp = /^{{.*}}$/
const h1Mapping = {};
let data = new Proxy({ "title": "" }, {
get(target, key, receiver) {
const result = Reflect.get(target, key, receiver)
return result;
},
set(target, key, val, receiver) {
const result = Reflect.set(target, key, val, receiver)
updateDataView();
return result;
},
})
window.onload = () => {
let hns = document.getElementById("h1title");
if (regexp.test(hns.innerHTML)) {
let val = hns.innerHTML;
// 置空
hns.innerHTML = "";
//put 对应关系
const a = val.replace("{{", "").replace("}}", "");
h1Mapping[a] = hns;
}
let ipt = document.getElementById("input");
ipt.addEventListener('input', function () {
let val = ipt.getAttribute("v-model");
const b = val += "=" + "'" + this.value + "'";
eval(b);
updateDataView();
})
}
function updateDataView() {
for (const key in h1Mapping) {
if (eval(key)!= undefined) {
h1Mapping[key].innerHTML = eval(key);
}
}
}
</script>
</html>
vue3弃用了Object.defineProperty,采用Proxy和Reflect来实现双向数据绑定,原因有以下几点:
1.Proxy可以代理整个对象,defineProperty只代理对象上的某个属性。
2.Proxy对代理对象的监听更加丰富。
3.Proxy代理对象会生成新的对象,不会修改被代理对象本身。
4.Proxy不兼容IE浏览器。