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

概念 双向绑定概念其实很简单,就是视图(View)的变化能实时让数据模型(Model)发生变化,而数据的变化也能实时更新到视图层。我们所说的单向数据绑定就是从数据到视图这一方向的关系。
分析 1、响应式数据
使用Object.defineProperty、Proxy对数据进行监听拦截。

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

vue3.0 开始 Proxy代替Object.defineProperty
let p = new Proxy(target, handler);

2、input事件监听
绑定事件处理函数,实时修改数据。
3、相关dom操作
将数据与相关dom节点绑定在一起,修改数据的时候对应的dom节点也应一起改变。
实现 1、html页面
v-model - 锐客网 姓名:{{name}}
年龄:{{age}}
【10分钟快速实现数据双向绑定】年龄:{{age}}


10分钟快速实现数据双向绑定
文章图片

2、VModel.js
(1)构造VModel
classVModel { constructor(el,data) { this.el = document.querySelector(el); //存放数据对象 this._data = https://www.it610.com/article/data; //存放绑定数据的dom节点 this.domPoll = {}; this.init(); } }

(2)初始化数据对象 使用Object.defineProperty
initData () { const _this = this; this.data = https://www.it610.com/article/{}; 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 = https://www.it610.com/article/{}; this.data = https://www.it610.com/article/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 = https://www.it610.com/article/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 = https://www.it610.com/article/input.value; //数据变化的时候会同步修改dom节点绑定的数据。 this.data[key] = _value; }

(4)完整代码
classVModel { constructor(el,data) { this.el = document.querySelector(el); this._data = https://www.it610.com/article/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 = https://www.it610.com/article/{}; // 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 = https://www.it610.com/article/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 = https://www.it610.com/article/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 = https://www.it610.com/article/input.value; this.data[key] = _value; // console.log(this.data); }setData(key,value){ this.data[key] = value; } }

10分钟快速实现数据双向绑定
文章图片

    推荐阅读