10分钟快速实现vue的数据双向绑定

简介: 10分钟快速实现vue的数据双向绑定

概念

双向绑定概念其实很简单,就是视图(View)的变化能实时让数据模型(Model)发生变化,而数据的变化也能实时更新到视图层。我们所说的单向数据绑定就是从数据到视图这一方向的关系。

分析

1、响应式数据

使用Object.defineProperty、Proxy对数据进行监听拦截。

//obj:必需。目标对象
//prop:必需。需定义或修改的属性的名字
//descriptor:必需。目标属性所拥有的特性
Object.defineProperty(obj, prop, descriptor)
• 4

vue3.0 开始 Proxy代替Object.defineProperty

let p = new Proxy(target, handler);

2、input事件监听

绑定事件处理函数,实时修改数据。

3、相关dom操作

将数据与相关dom节点绑定在一起,修改数据的时候对应的dom节点也应一起改变。

实现

1、html页面

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>v-model</title>
  </head>
  <body>
    <div id="app">
      <input type="text" v-model="name" placeholder="姓名"/>
      <input type="text" v-model="age" placeholder="年龄"/>
      <div>
        <p>
          姓名:<span>{{name}}</span>
        </p>
        <p>
          年龄:<span>{{age}}</span>
        </p>
        <p>
          <p>
            年龄:<span>{{age}}</span>
          </p>
        </p>
      </div>
      <button id="btn">修改名字</button>
    </div>
  </body>
  <script src="./VModel.js"></script>
  <script>
    const app = new VModel('#app',{
      name:'',
      age:''
    });
    document.getElementById('btn').addEventListener('click',function() {
      app.setData('name','名字改变了');
    })
  </script>
</html>

2、VModel.js

(1)构造VModel
class  VModel {
  constructor(el,data) {
    this.el = document.querySelector(el);
    //存放数据对象
    this._data = data;
    //存放绑定数据的dom节点
    this.domPoll = {};
    this.init();
  }
}
(2)初始化数据对象
使用Object.defineProperty
initData () {
  const _this = this;
  this.data = {};
  for(let key in this._data){
    Object.defineProperty(this.data,key,{
      get(){
        console.log("获取数据",key,_this._data[key]);
        return _this._data[key];
      },
      set(newVal){
        console.log("设置数据",key,newVal);
        _this.domPoll[key].innerText = newVal;
        _this._data[key] = newVal;
      }
    });
  }
}
使用Proxy
initData () {
  const _this = this;
  this.data = {};
  this.data = new Proxy(this.data,{
    get(target,key){
      return Reflect.get(target,key);
    },
    set(target,key,value){
      // _this.domPoll[key].innerText = value;
      _this.domPoll[key].forEach(item => {
        item.innerText = value;
      })
      return Reflect.set(target,key,value);
    }
  })
}
(3)绑定dom节点
bindDom(el){
  const childNodes = el.childNodes;
  childNodes.forEach(item => {
    //nodeType为3时该dom节点为文本节点
    if(item.nodeType === 3){
      const _value = item.nodeValue;
      if(_value.trim().length){
        //匹配是否有两个花括号包裹的数据
        let  _isValid = /\{\{(.+?)\}\}/.test(_value);
        if(_isValid){
          const _key = _value.match(/\{\{(.+?)\}\}/)[1].trim();
          // this.domPoll[_key] = item.parentNode;
          //一个数据可以被多个dom节点绑定,所以应该用数组来进行保存
            //未定义时先初始化
            if(!this.domPoll[_key]) this.domPoll[_key] = [];
            this.domPoll[_key].push(item.parentNode);
          //替换绑定的值
          item.parentNode.innerText = this.data[_key] || undefined;
        }
      }
    }
    //递归遍历子节点
    item.childNodes && this.bindDom(item);
  })
}

####(4)输入框数据绑定

bindInput(el){
  //获取input所有元素节点
  const _allInput = el.querySelectorAll('input');
  _allInput.forEach(input => {
    const _vModel = input.getAttribute('v-model');
    //判断是否有v-model属性
    if(_vModel){
//监听输入事件      input.addEventListener('keyup',this.handleInput.bind(this,_vModel,input),false);
    }
  })
}
handleInput(key,input){
  const _value = input.value;
  //数据变化的时候会同步修改dom节点绑定的数据。
  this.data[key] = _value;
}
(4)完整代码
class  VModel {
  constructor(el,data) {
    this.el = document.querySelector(el);
    this._data = data;
    this.domPoll = {};
    this.init();
  }
  init(){
    this.initData();
    this.initDom();
  }
  initDom(){
    this.bindDom(this.el);
    this.bindInput(this.el);
    console.log('domPoll',this.domPoll);
  }
  initData () {
    const _this = this;
    this.data = {};
    // for(let key in this._data){
    //  Object.defineProperty(this.data,key,{
    //    get(){
    //      console.log("获取数据",key,_this._data[key]);
    //      return _this._data[key];
    //    },
    //    set(newVal){
    //      console.log("设置数据",key,newVal);
    //      _this.domPoll[key].innerText = newVal;
    //      _this._data[key] = newVal;
    //    }
    //  });
    // }
    this.data = new Proxy(this.data,{
      get(target,key){
        return Reflect.get(target,key);
      },
      set(target,key,value){
        // _this.domPoll[key].innerText = value;
        _this.domPoll[key].forEach(item => {
          item.innerText = value;
        })
        return Reflect.set(target,key,value);
      }
    })
  }
  bindDom(el){
    const childNodes = el.childNodes;
    childNodes.forEach(item => {
      if(item.nodeType === 3){
        const _value = item.nodeValue;
        if(_value.trim().length){
          let  _isValid = /\{\{(.+?)\}\}/.test(_value);
          if(_isValid){
            const _key = _value.match(/\{\{(.+?)\}\}/)[1].trim();
            // this.domPoll[_key] = item.parentNode;
            if(!this.domPoll[_key]) this.domPoll[_key] = [];
            this.domPoll[_key].push(item.parentNode);
            item.parentNode.innerText = this.data[_key] || undefined;
          }
        }
      }
      item.childNodes && this.bindDom(item);
    })
  }
  bindInput(el){
    const _allInput = el.querySelectorAll('input');
    _allInput.forEach(input => {
      const _vModel = input.getAttribute('v-model');
      if(_vModel){
        input.addEventListener('keyup',this.handleInput.bind(this,_vModel,input),false);
      }
    })
  }
  handleInput(key,input){
    const _value = input.value;
    this.data[key] = _value;
    // console.log(this.data);
  }
  setData(key,value){
    this.data[key] = value;
  }
}

目录
相关文章
|
6天前
|
JavaScript
vue使用iconfont图标
vue使用iconfont图标
51 1
|
2月前
|
缓存 JavaScript UED
Vue3中v-model在处理自定义组件双向数据绑定时有哪些注意事项?
在使用`v-model`处理自定义组件双向数据绑定时,要仔细考虑各种因素,确保数据的准确传递和更新,同时提供良好的用户体验和代码可维护性。通过合理的设计和注意事项的遵循,能够更好地发挥`v-model`的优势,实现高效的双向数据绑定效果。
146 64
|
17天前
|
JavaScript 关系型数据库 MySQL
基于VUE的校园二手交易平台系统设计与实现毕业设计论文模板
基于Vue的校园二手交易平台是一款专为校园用户设计的在线交易系统,提供简洁高效、安全可靠的二手商品买卖环境。平台利用Vue框架的响应式数据绑定和组件化特性,实现用户友好的界面,方便商品浏览、发布与管理。该系统采用Node.js、MySQL及B/S架构,确保稳定性和多功能模块设计,涵盖管理员和用户功能模块,促进物品循环使用,降低开销,提升环保意识,助力绿色校园文化建设。
|
2月前
|
JavaScript API 开发者
Vue是如何进行组件化的
Vue是如何进行组件化的
|
2月前
|
JavaScript 前端开发 开发者
Vue是如何进行组件化的
Vue是如何进行组件化的
|
2月前
|
存储 JavaScript 前端开发
介绍一下Vue的核心功能
介绍一下Vue的核心功能
|
8月前
|
JavaScript API
【vue实战项目】通用管理系统:api封装、404页
【vue实战项目】通用管理系统:api封装、404页
82 3
|
8月前
|
人工智能 JavaScript 前端开发
毕设项目-基于Springboot和Vue实现蛋糕商城系统(三)
毕设项目-基于Springboot和Vue实现蛋糕商城系统
|
8月前
|
JavaScript Java 关系型数据库
毕设项目-基于Springboot和Vue实现蛋糕商城系统(一)
毕设项目-基于Springboot和Vue实现蛋糕商城系统
203 0
|
8月前
|
JavaScript 前端开发 API
Vue3+Vite+TypeScript常用项目模块详解
现在无论gitee还是github,越来越多的前端开源项目采用Vue3+Vite+TypeScript+Pinia+Elementplus+axios+Sass(css预编译语言等),其中还有各种项目配置比如eslint 校验代码工具配置等等,而我们想要进行前端项目的二次开发,就必须了解会使用这些东西,所以作者写了这篇文章进行简单的介绍。
158 0
Vue3+Vite+TypeScript常用项目模块详解

热门文章

最新文章