前段汇总之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>
相关推荐
不是吧这都有重名21 分钟前
利用systemd启动部署在服务器上的web应用
运维·服务器·前端
霸王蟹21 分钟前
React中巧妙使用异步组件Suspense优化页面性能。
前端·笔记·学习·react.js·前端框架
Maỿbe30 分钟前
利用html制作简历网页和求职信息网页
前端·html
森叶1 小时前
Electron 主进程中使用Worker来创建不同间隔的定时器实现过程
前端·javascript·electron
霸王蟹1 小时前
React 19 中的useRef得到了进一步加强。
前端·javascript·笔记·学习·react.js·ts
霸王蟹1 小时前
React 19版本refs也支持清理函数了。
前端·javascript·笔记·react.js·前端框架·ts
繁依Fanyi1 小时前
ColorAid —— 一个面向设计师的色盲模拟工具开发记
开发语言·前端·vue.js·编辑器·codebuddy首席试玩官
codelxy1 小时前
vue引用cesium,解决“Not allowed to load local resource”报错
javascript·vue.js
程序猿阿伟2 小时前
《社交应用动态表情:RN与Flutter实战解码》
javascript·flutter·react native
明似水2 小时前
Flutter 开发入门:从一个简单的计数器应用开始
前端·javascript·flutter