对 Angular 和 Vue 等前端框架中的数据单向绑定和双向绑定很感兴趣,于是自己试着模拟了一个。采用 AngularJS 的语法,在标签中添加 yjb-bind表示单向绑定,yjb-model表示双向绑定。
数据监听
数据绑定首要要实现的就是数据监听,而数据监听的方法有很多,这里我采用了 setter 方法。
废话少说,上代码!
HTML部分:
<input type="text" yjb-model="message" placeholder="双向绑定message" id="inp">
<input type="text" yjb-bind="message" placeholder="单向绑定message">
<div yjb-bind="message"></div>
<hr>
<input type="text" yjb-model="text" placeholder="双向绑定text">
<input type="text" yjb-bind="text" placeholder="单向绑定text">
<div yjb-bind="text"></div>
JS部分
var scope = {
message: "哈哈哈",text: "嘟嘟嘟"
};
var keys = Object.keys(scope); // ["message","text"]
// 监听数据变化
keys.forEach(function (key) {
// 定义一组不可枚举的属性
Object.defineProperty(scope,"__" + key,{
value: scope[key],enumerable: false,writable: true
});
// 监听数据修改
Object.defineProperty(scope,key,{
set: function (val) {
// 将值赋给刚才创建的属性(若赋给当前属性,会递归触发 set 事件,导致堆栈溢出)
this["__" + key] = val;
// 修改时触发更新函数
updateData(key);
},get: function () {
return this["__" + key]
}
});
updateData(key);
});
// 事件委托,通过全局监听键盘事件来监听视图中的数据修改
document.addEventListener("input",function (e) {
var index = keys.indexOf(e.target.getAttribute("yjb-model"));
if (index != -1) {
var key = keys[index];
scope[key] = e.target.value;
}
});
// 更新数据
function updateData(key) {
document.querySelectorAll("[yjb-model=" + key + "]").forEach(function (ele) {
ele.value = scope[key];
});
document.querySelectorAll("[yjb-bind=" + key + "]").forEach(function (ele) {
if (ele.tagName == "INPUT" || ele.tagName == "TEXTAREA") {
ele.value = scope[key];
} else {
ele.innerHTML = scope[key];
}
})
}
上述代码可以简单的模拟数据的单向绑定和双向绑定,但是,仅限于 scope
对象下的数据类型为基本类型的属性,如果是对象之类的,就不行了,那样会复杂一些,有空再研究~
更新数据那部分性能很差,可以无视,只是为了把数据扔到 HTML 里而已~现在正在研究虚拟 DOM 树~研究 Vue 源码~刚看到尤雨溪说 Vue 就是使用的 getter 和 setter 实现的数据绑定~