参考vue的关键字:v-model绑定值,{{}},显示值
目录
简单实现双向绑定
使用Proxy优化双向绑定
动态更新值
简单实现双向绑定
新建html5模板:
<!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>
</body>
</html>
然后声明一个H1标签用于展示数据,声明一个input用于输入数据
显示title:<h1>{{title}}</h1>
输入title:<input type="text" v-model="title" />
监听input的值改变,然后设置H1标签新值。
let inputs = document.querySelectorAll("input");
let h1s = document.querySelectorAll("h1");
inputs[0].addEventListener("input",()=>{
h1s[0].innerHTML=inputs[0].value;
})
这样只要修改input的值,H1标签就可以实时更新了。
效果如下图所示:
现在有个问题就是页面初始化或者刷新的时候会出现{{}}里面的变量值,我们可以在页面加载事件的时候把该值置空。代码如下:
let inputs = document.querySelectorAll("input");
let h1s = document.querySelectorAll("h1");
window.onload = ()=>{
for (let i=0; i<h1s.length; i++) {
//置空
h1s[i].innerHTML="";
}
}
inputs[0].addEventListener("input",()=>{
h1s[0].innerHTML=inputs[0].value;
})
上面实现并没有跟变量绑定,所以声明一个变量如下:
let data = {
"title":""
};
当input的值变化的时候,修改title的值。然后更改H1的标签值
修改监听事件
inputs[0].addEventListener("input",()=>{
data.title=inputs[0].value;
h1s[0].innerHTML=data.title;
})
这样就实现了数据双向绑定
使用Proxy优化双向绑定
上面的实现后有一个问题,该变量只跟“唯一”的input进行数据双向绑定了,如果其它逻辑修改了变量值,就不能更新页面了。如下代码所示:
<body>
显示title:<h1>{{title}}</h1>
输入title:<input type="text" v-model="title" />
<input type="button" value="title+1" />
</body>
inputs[1].addEventListener("click",()=>{
data.title+="_1"
})
当点击新的按钮,title变化后,H1标签并没有变化。所以就要引入Proxy监听对象。
如下修改title的值:
// let data = {"title":""};
let data = new Proxy({"title":""}, {
get(target, key, receiver) {
const result = Reflect.get(target, key, receiver)
console.log('get', key)
return result; // 返回设置结果
},
set(target, key, val, receiver) {
const result = Reflect.set(target, key, val, receiver)
console.log('set', key, val)
// 更新H1标签
h1s[0].innerHTML = val;
return result;
},
deleteProperty(target, key) {
const result = Reflect.deleteProperty(target, key)
console.log('delete property', key)
return result;
}
})
完整代码如下:
<!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>{{title}}</h1>
输入title:<input type="text" v-model="title" />
<input type="button" value="title+1" />
</body>
<script>
let data = new Proxy({"title":""}, {
get(target, key, receiver) {
const result = Reflect.get(target, key, receiver)
console.log('get', key)
return result; // 返回设置结果
},
set(target, key, val, receiver) {
const result = Reflect.set(target, key, val, receiver)
console.log('set', key, val)
// 更新H1标签
h1s[0].innerHTML = val;
return result;
},
deleteProperty(target, key) {
const result = Reflect.deleteProperty(target, key)
console.log('delete property', key)
return result;
}
})
let inputs = document.querySelectorAll("input");
let h1s = document.querySelectorAll("h1");
window.onload = () => {
for (let i = 0; i < h1s.length; i++) {
h1s[i].innerHTML = "";
}
}
inputs[0].addEventListener("input", () => {
data.title = inputs[0].value;
h1s[0].innerHTML = data.title;
})
inputs[1].addEventListener("click", () => {
data.title += "_1"
})
</script>
</html>
效果如下图:
动态更新值
上面的方式有很多耦合代码,如果有多个双向绑定就要写一堆代码,所以可以再页面加载的时候遍历双向绑定的属性进行关联。
声明对象存储参数和标签的对应关系以及判断{{}}的正则
var regexp = /^{{.*}}$/
const h1Mapping = {};
首先页面加载事件遍历所有的H1的{{}}(这里仅以H1展示值)
let h1s = document.querySelectorAll("h1");
......
for (let index = 0; index < h1s.length; index++) {
const h1 = h1s[index];
if(regexp.test(h1.innerHTML)){
let val = h1.innerHTML;
// 置空
h1.innerHTML="";
h1Mapping[val.replace("{{","").replace("}}","")] = h1 ;
}
}
然后遍历所有的input包含v-model的:
let inputs = document.querySelectorAll("input[v-model]");
for (let index = 0; index < inputs.length; index++) {
const input = inputs[index];
input.addEventListener('input', function () {
let val = input.getAttribute("v-model");
eval(val += "=" + "'" + this.value + "'");
// updateDataView();
})
}
例如input的标签如下:
v-model="data.title"
值改变的时候就会执行js代码
// data.title 等于 input的值
data.title=val
这样就实现了包含v-model的input的值改变后就会对应更新对象的值;
上面保存了变量值和显示标签的对应关系,新增一个方法更新全部视图,如下:
function updateDataView(_key) {
if (_key) {
if (eval(_key)!= undefined) {
h1Mapping[_key].innerHTML = eval(_key);
}
} else {
for (const key in h1Mapping) {
if (eval(key)!= undefined) {
h1Mapping[key].innerHTML = eval(key);
}
}
}
}
当input值改变的时候,调用改方法,就可以刷新页面。
然后再Proxy值改变监听哪里更新页面,而不是写固定的第一个H1更新值。
let data = new Proxy({ "title": "" }, {
get(target, key, receiver) {
const result = Reflect.get(target, key, receiver)
console.log('get', key)
return result; // 返回设置结果
},
set(target, key, val, receiver) {
const result = Reflect.set(target, key, val, receiver)
console.log('set', key, val)
// 更新html
updateDataView();
return result;
},
deleteProperty(target, key) {
const result = Reflect.deleteProperty(target, key)
console.log('delete property', key)
return result;
}
})
HTML代码也更改为调用属性,顺便新增一个name属性,添加一个按钮改变值属性的
显示title:<h1>{{data.title}}</h1>
输入title:<input type="text" v-model="data.title" />
显示name:<h1>{{data.name}}</h1>
输入name:<input type="text" v-model="data.name" />
<input type="button" value="更改值" onclick="changeValue()" />
function changeValue(){
for (const key in data) {
data[key] = "被改变的值";
}
}
运行页面效果如下:
完整代码如下:
<!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>{{data.title}}</h1>
输入title:<input type="text" v-model="data.title" />
<br />
显示name:<h1>{{data.name}}</h1>
输入name:<input type="text" v-model="data.name" />
<input type="button" value="更改值" onclick="changeValue()" />
</body>
<script>
let data = new Proxy({ "title": "" }, {
get(target, key, receiver) {
const result = Reflect.get(target, key, receiver)
console.log('get', key)
return result; // 返回设置结果
},
set(target, key, val, receiver) {
const result = Reflect.set(target, key, val, receiver)
console.log('set', key, val)
// 更新html
updateDataView();
return result;
},
deleteProperty(target, key) {
const result = Reflect.deleteProperty(target, key)
console.log('delete property', key)
return result;
}
})
var regexp = /^{{.*}}$/
const h1Mapping = {};
window.onload = () => {
let h1s = document.querySelectorAll("h1");
for (let index = 0; index < h1s.length; index++) {
const h1 = h1s[index];
if (regexp.test(h1.innerHTML)) {
let val = h1.innerHTML;
// 置空
h1.innerHTML = "";
//put 对应关系
h1Mapping[val.replace("{{", "").replace("}}", "")] = h1;
}
}
let inputs = document.querySelectorAll("input[v-model]");
for (let index = 0; index < inputs.length; index++) {
const input = inputs[index];
input.addEventListener('input', function () {
let val = input.getAttribute("v-model");
eval(val += "=" + "'" + this.value + "'");
updateDataView();
})
}
}
function updateDataView(_key) {
if (_key) {
if (eval(_key)!= undefined) {
h1Mapping[_key].innerHTML = eval(_key);
}
} else {
for (const key in h1Mapping) {
if (eval(key)!= undefined) {
h1Mapping[key].innerHTML = eval(key);
}
}
}
}
function changeValue(){
for (const key in data) {
data[key] = "被改变的值";
}
}
</script>
</html>