前段汇总之JS实现数据双向绑定

参考vue的关键字:v-model绑定值,{{}},显示值


目录

简单实现双向绑定

使用Proxy优化双向绑定

动态更新值


简单实现双向绑定

新建html5模板:

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>
  
</body>
</html>

然后声明一个H1标签用于展示数据,声明一个input用于输入数据

html 复制代码
  显示title:<h1>{{title}}</h1>
  输入title:<input  type="text" v-model="title" />

监听input的值改变,然后设置H1标签新值。

javascript 复制代码
  let inputs = document.querySelectorAll("input");
  let h1s = document.querySelectorAll("h1");
  inputs[0].addEventListener("input",()=>{
    h1s[0].innerHTML=inputs[0].value;
  })

这样只要修改input的值,H1标签就可以实时更新了。

效果如下图所示:

现在有个问题就是页面初始化或者刷新的时候会出现{{}}里面的变量值,我们可以在页面加载事件的时候把该值置空。代码如下:

javascript 复制代码
  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;
  })

上面实现并没有跟变量绑定,所以声明一个变量如下:

javascript 复制代码
let data = {
    "title":""
  };

当input的值变化的时候,修改title的值。然后更改H1的标签值

修改监听事件

javascript 复制代码
  inputs[0].addEventListener("input",()=>{
    data.title=inputs[0].value;
    h1s[0].innerHTML=data.title;
  })

这样就实现了数据双向绑定

使用Proxy优化双向绑定

上面的实现后有一个问题,该变量只跟"唯一"的input进行数据双向绑定了,如果其它逻辑修改了变量值,就不能更新页面了。如下代码所示:

html 复制代码
<body>
  显示title:<h1>{{title}}</h1>
  输入title:<input  type="text" v-model="title" />
  <input type="button" value="title+1" />
</body>
javascript 复制代码
  inputs[1].addEventListener("click",()=>{
    data.title+="_1"
  })

当点击新的按钮,title变化后,H1标签并没有变化。所以就要引入Proxy监听对象。

如下修改title的值:

javascript 复制代码
// 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;
    }
})

完整代码如下:

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>{{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>

效果如下图:

动态更新值

上面的方式有很多耦合代码,如果有多个双向绑定就要写一堆代码,所以可以再页面加载的时候遍历双向绑定的属性进行关联。

声明对象存储参数和标签的对应关系以及判断{{}}的正则

javascript 复制代码
var regexp = /^{{.*}}$/
const h1Mapping  = {};

首先页面加载事件遍历所有的H1的{{}}(这里仅以H1展示值)

javascript 复制代码
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的:

javascript 复制代码
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的标签如下:

javascript 复制代码
v-model="data.title"

值改变的时候就会执行js代码

javascript 复制代码
// data.title 等于 input的值
data.title=val

这样就实现了包含v-model的input的值改变后就会对应更新对象的值;

上面保存了变量值和显示标签的对应关系,新增一个方法更新全部视图,如下:

javascript 复制代码
  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更新值。

javascript 复制代码
  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属性,添加一个按钮改变值属性的

html 复制代码
显示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()" />
javascript 复制代码
  function changeValue(){
    for (const key in data) {
      data[key] = "被改变的值";
    }
  }

运行页面效果如下:

完整代码如下:

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>{{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>
相关推荐
QGC二次开发3 分钟前
Vue3 : Pinia的性质与作用
前端·javascript·vue.js·typescript·前端框架·vue
云草桑15 分钟前
逆向工程 反编译 C# net core
前端·c#·反编译·逆向工程
布丁椰奶冻20 分钟前
解决使用nvm管理node版本时提示npm下载失败的问题
前端·npm·node.js
Leyla1 小时前
【代码重构】好的重构与坏的重构
前端
影子落人间1 小时前
已解决npm ERR! request to https://registry.npm.taobao.org/@vant%2farea-data failed
前端·npm·node.js
世俗ˊ1 小时前
CSS入门笔记
前端·css·笔记
子非鱼9211 小时前
【前端】ES6:Set与Map
前端·javascript·es6
6230_1 小时前
git使用“保姆级”教程1——简介及配置项设置
前端·git·学习·html·web3·学习方法·改行学it
想退休的搬砖人1 小时前
vue选项式写法项目案例(购物车)
前端·javascript·vue.js
加勒比海涛2 小时前
HTML 揭秘:HTML 编码快速入门
前端·html