现在mvvm框架火得不行,小编我最近也开始研究vue,相比jquery框架,vue的面向数据编程的确可以让我们摆脱操作dom的痛苦专注逻辑本身。
javascript
无论学什么框架,我都喜欢研究底层的东西,vue最核心的功能就是双向绑定,经过几天研究,同javascript实现了一个双向绑定功能。
双向数据绑定底层的思想非常的基本,分为三个步骤:
1.需要一个方法来识别哪个UI元素被绑定了相应的属性
2.需要监视属性和UI元素的变化
3.需要将所有变化传播到绑定的对象和元素
方法一:利用发布订阅模式,订阅数据变更
Html代码:
javascript代码
function DataBinder(object_id) {
//创建一个简单地PubSub对象
var pubSub = {
callbacks: {},
on: function (msg, callback) {
this.callbacks[msg] = this.callbacks[msg] || [];
this.callbacks[msg].push(callback);
},
publish: function (msg) {
this.callbacks[msg] = this.callbacks[msg] || [];
for (var i = 0, len = this.callbacks[msg].length; i < len; i++) {
this.callbacks[msg][i].apply(this, Array.prototype.slice.call(arguments, 1));
}
}
};
var data_attr = "data-bind-" + object_id,
view_attr = 'data-' + object_id,
message = object_id + ":change";
var changeHandler = function (e) {
var target = e.target || e.srcElemnt, //IE8兼容
prop_name = target.getAttribute(data_attr);
if (prop_name && prop_name !== "") {
pubSub.publish(message, target, prop_name, target.value);
}
};
//监听变化事件并代理到PubSub
if (document.addEventListener) {
document.addEventListener("keyup", changeHandler, false);
} else {
//IE8使用attachEvent而不是addEventListener
document.attachEvent("keyup", changeHandler);
}
//PubSub将变化传播到所有绑定元素
pubSub.on(message, function (event, prop_name, new_val) {
var elements = document.querySelectorAll("[" + view_attr + "=" + prop_name + "]"), tag_name;
for (var i = 0, len = elements.length; i < len; i++) {
tag_name = elements[i].tagName.toLowerCase();
if (tag_name === "input" || tag_name === "textarea" || tag_name === "select") {
elements[i].value = new_val;
} else {
elements[i].innerHTML = new_val;
}
}
});
return pubSub;
}
DataBinder('id');
方法二:数据劫持 。
html代码:
借助 Object.defineProperty 方法, Object.defineProperty 方法可以为任何已存在的属性重新定义get与set方法。
(function(window, undefined) {
var addEvent = (function () {
if(window.addEventListener) {
return function (el, type, fn) {
if(el && el.nodeName || el === window) {
el.addEventListener(type, fn, false)
}else if(el.length) {
for(var item of el) {
addEvent(item, type, fn);
}
}
}
}else {
return function (el, type, fn) {
if(el && el.nodeName || el === window) {
el.attachEvent('on' + type, function () {
return fn.call(el, event);
})
}else if(el.length) {
for(var item of el) {
addEvent(item, type, fn);
}
}
}
}
})();
var Hue = function(opt) {
var el = document.querySelector(opt.el);
var data = opt.data || {};
this.el = el;
this.data = data;
this.bindText();
this.bindModel();
return this;
};
Hue.prototype = {
constructor: Hue,
// **前端数据劫持**
defineObj: function(obj, prop, value) {
var _value = value || '', _this = this;
try {
Object.defineProperty(obj, prop, {
get: function() {
return _value;
},
set: function(newVal) {
_value = newVal;
_this.bindText();
},
enumerable: true,
configurable: true
});
} catch (error) {
// IE8+ 才开始支持defineProperty,这也是Vue.js不支持IE8的原因
console.log("Browser must be IE8+ !");
}
},
bindModel: function() {
var modelDOMs = this.el.querySelectorAll('[h-model]'), lenModel = modelDOMs.length;
var _this = this, i, propModel;
for (i = 0; i < lenModel; i++) {
propModel = modelDOMs[i].getAttribute('h-model');
//init value
modelDOMs[i].value = this.data[propModel] || '';
// 前端数据劫持
this.defineObj(this.data, propModel);
}
addEvent(modelDOMs, 'keyup', function (e) {
_this.data[propModel] = e.target.value;
})
},
bindText: function() {
var textDOMs = this.el.querySelectorAll('[h-text]'),
lenText = textDOMs.length,
prppText,
j;
for (j = 0; j < lenText; j++) {
propText = textDOMs[j].getAttribute('h-text');
textDOMs[j].innerHTML = this.data[propText] || '';
}
}
};
window.Hue = Hue;
})(window);
// test...
new Hue({
el: '.app',
data: {
demo: 'Kenny'
}
});
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/69946831/viewspace-3167967/ ,如需转载,请注明出处,否则将追究法律责任。