简单介绍
我的理解,所谓的双向绑定,其实就是将Model和View绑定在一起,任何一方改变的同时,改变另外一方。
在流行框架中,react是单向绑定(只支持Model改变=>View改变),要实现双向绑定得加value和onChange事件从而实现(View改变=>调起事件=>改变Model)。
而vue是双向绑定的,因为它事先已经帮我们绑定好了事件。
什么是Model
我理解为Model就是一个JS对象,用来存储页面中的数据。
什么是View
我理解是页面中所显示的DOM对象的集合。
怎么实现双向绑定呢?
Object.defineProperty()
这个建议去看一下红宝书的介绍可以帮助快速理解。或者点击MND
主要使用到了它的访问器属性:get和set
Object.defineProperty()版本
首先我们准备一下界面,比较简单,左侧是input框,右边是model转成的字符串。我们会使用到console来改变model的属性值,来测试是否会改变DOM
源码在这
demo在这
界面代码(index.html)如下:
bidreactional binding
View:
Model:
接下来是实现绑定逻辑,我们都写在了index.js中,都有对应的注释。
// 获取DOM节点
var view = document.getElementById('view');
var model = document.getElementById('model');
// 设置model对象
var data = {};
// 设置get函数的中转站,封装后可以去掉
let temp = 0;
//在data对象中定义number属性,并给他赋值两个访问器属性,来代理或者说劫持number的值的获取与设置
Object.defineProperty(data, "number", {
//可枚举,这个主要是用来将Model显示在前端的,可以省去
enumerable:true,
// 获取值时的处理方法 就相当于代理执行获取值的操作,返回什么都又他决定,这里不能return data.number会造成无限循环的
get: function () {
return temp;
},
// data的number值发生变化时调用
set: function (value) {
// 改变View节点的值
view.value = value;
// 将值存在temp中,在get时要用到
temp = value;
// 这个主要是用来将Model显示在前端的,可以省去
model.innerHTML = `"data":${JSON.stringify(data)}`;
},
})
// 绑定事件,当view改变时将改变的值赋值给data对象中的number属性
view.addEventListener("keyup", function (event) {
data.number = event.target.value;
})
以上就可以实现一个基于单个输入框的双向绑定了。这里放代码链接和demo链接!假设我现在有三个输入框怎么办呢?我要整个流程再来三次吗?其实不用的,我们可以对流程封装一下。
源码在这
demo在这
index.html
.....
View:
username:
password:
sex:
......
index.js
// model 是用来显示model字符串的 可省去
var model = document.getElementById('model');
// model对象
var data = {};
// 所有input的id
const keys = ["username", "password", "sex"];
// 给每个id都实现上双向绑定
keys.forEach(item => {
// 封装后绑定View和Model的方法
bindViewAndModel(item, "", document.getElementById(item))
})
function bindViewAndModel(key, val, dom) {
// Model => View
Object.defineProperty(data, key, {
// 这里只是为了前端展示model 可以省去
enumerable: true,
get: function () {
return val; // 去掉了temp
},
set: function (newValue) {
val = newValue;
dom.value = newValue;
// 这里只是为了前端展示model 可以省去
model.innerHTML = JSON.stringify(data);
},
})
// View => Model
dom.addEventListener("keyup", function (event) {
data[key] = event.target.value;
})
}
到这里我们就能实现多个input框的双向绑定了,可以在这里测试一下。
其实这里还会设计不同的表单元素的变化,这里就不再深入了。
接下来探究一下proxy。为什么会用proxy替换掉Object.defineProperty()呢?因为defineProperty无法监听数组变化(这里我还在试验中),也只能劫持对象的属性。而proxy而劫持整个对象。
Proxy版本双向绑定
源码在这
demo在这
index.html 同上
index.js
// 这里只是为了前端展示model 可以省去
var model = document.getElementById('model');
// 所有dom的id
const domKeys =["username","password","sex"];
// 枚举信息 根据 {domkey:dom}
const domEnum = {};
// model
var data = {};
// proxy 代理整个data
const proxy = new Proxy(data, {
// taget 即为代理的对象 prop为属性值
get: function (target, prop) {
return target[prop];
},
// value为新值
set: function (target, prop, value) {
target[prop] = value;
domEnum[prop+'Dom'].value = target[prop];
// 这里只是为了前端展示model 可以省去
model.innerHTML = JSON.stringify(data);
}
})
// 加上key事件
domKeys.forEach(item=>{
const dom = document.getElementById(item);
domEnum[item+'Dom'] = dom;
dom.addEventListener("keyup", function (event) {
proxy[item] = event.target.value;
})
})
本文主要帮助理解了双向绑定的原理,以及实现双向绑定的两种方法。如有不对之处还望帮忙指出~~