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}}
文章图片
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;
}
}
文章图片
推荐阅读
- 关于QueryWrapper|关于QueryWrapper,实现MybatisPlus多表关联查询方式
- MybatisPlus使用queryWrapper如何实现复杂查询
- python学习之|python学习之 实现QQ自动发送消息
- 孩子不是实现父母欲望的工具——林哈夫
- opencv|opencv C++模板匹配的简单实现
- Node.js中readline模块实现终端输入
- java中如何实现重建二叉树
- 人脸识别|【人脸识别系列】| 实现自动化妆
- paddle|动手从头实现LSTM
- pytorch|使用pytorch从头实现多层LSTM