如何实现一个简单的MVVM框架

接触过web开发的同学想必都接触过MVVM,业界著名的MVVM框架就有AngelaJS。今天闲来无事,决定自己实现一个简单的MVVM框架玩一玩。所谓简单,就是仅仅实现一个骨架,仅表其意,不摹其形。

分析

MVVM最大的特点莫过于双向绑定了,数据的变化能及时更新到视图上,同时视图的变化也能及时的更新到数据中。
那么怎么实现这样的双向绑定呢?最直接的方式莫过于事件了。
所以,我们需要实现一个事件的订阅与分发机制,这个功能很常见,网上搜一搜一大堆。
有了这么一套事件的订阅与分发机制,我们就可以通过它将Model和View关联起来,这样不管是Model还是View有变化,都可以通过事件通知到对方了。

实现

首先要实现一套事件的订阅与分发机制,直接贴代码了:

function Event() {  
    this.handlers = {};  
}

Event.prototype.on = function (eventName, handler) {
	if (!this.handlers) {
		this.handlers = {};
	}
	if (this.handlers[eventName]) {
		this.handlers[eventName].push(handler);
	} else {
		this.handlers[eventName] = [handler];
	}
}

Event.prototype.fire = function (eventName, eventData) {
	if (this.handlers[eventName]) {
		this.handlers[eventName].forEach(function (handler) {
			handler(eventData);
		});
	}
}

Event.prototype.off = function (eventName, handler) {
	if (this.handlers[eventName]) {
		for (var i = 0; i < this.handlers[eventName].length; i++) {
			if (this.handlers[eventName][i] === handler) {
				this.handlers[eventName].splice(i, 1);
			}
		}
	}
}  

上诉代码实现了某个事件的监听、触发与移除操作。

有了事件,如何将其与View和Model结合起来呢? 继承。

function Model(data) {
	this.data = data;
}

Model.prototype = new Object(Event.prototype);
Model.prototype.constructor = Model;

Model.prototype.set = function (key, value) {
	if (this.data[key]) {
		this.data[key] = value;
	}
	this.fire("change", value);
}


Model.prototype.get = function (key) {
	console.log("key: ", key, " value: ", this.data[key]);
}
function View(model, el) {
	this.el = el;
	this.model = model;

	this.init();
}

View.prototype.init = function () {
	var me = this;
	this.model.on("change", function (value) {
		me.model.get("value");
		if (me.el.type === "text") {
			me.el.value = value;
		} else {
			me.el.innerText = value;
		}		
	});
	if (this.el.type === "text") {
		this.el.addEventListener("change", function () {
			me.model.set("value", this.value);
		});
	} else {
		this.el.addEventListener("click", function () {
			var num = new Number(this.innerText || 0);
			me.model.set("value", num + 1);
		});
	}

}

View中为了简单处理,直接进行了硬编码,实际应用过程中需要详细处理。

到此,Model和View都有了,下面再加一段代码将他们关联起来:


function MVVM() {
	this.cache = {};
}

MVVM.prototype.bind = function (data, el) {
	var model = new Model(data);
	var view = new View(model, el);
	var key = "key_" + (new Date()).getTime();
	this.cache[key] = {
		model: model,
		view: view
	};
}

测试代码如下:

< !DOCTYPE html>
< html>
< head>
	< title>MVVM</title>
	< script type="text/javascript" src="./Event.js"></script>
	< script type="text/javascript" src="./Model.js"></script>
	< script type="text/javascript" src="./View.js"></script>
	< style type="text/css">
		#inputId {
			width: 300px;
			height: 30px;
			border: 1px solid grey;
			margin: 10px;
		}
		#textId {
			width: 300px;
			height: 100px;
			text-align: center;
			border: 1px solid black;
			line-height: 100px;
			font-size: 20px;
		}
	< /style>
< /head>
< body>
< div>
	< input id="inputId" type="text"></input>
	< div id="textId">0</div>
< /div>
< script type="text/javascript" src="./index.js"></script>
< script type="text/javascript">
var mvvm = new MVVM();
mvvm.bind({
	value: "text input"
}, document.getElementById("inputId"));
mvvm.bind({
	value: "div text"
}, document.getElementById("textId"));
< /script>
< /body>
< /html>

测试代码中绑定了一个输入框和一个div,当输入框中值发生改变时,Model中的值也会相应改变(查看控制台打印信息)。当点击div时,div中的文本数字会加一,对应Model中的数据也会改变。

到此所有的功能就实现完了,虽说简单了点,但是基本意思算是都到了,收工~~。

posted @ 2016-03-26 16:28  tbingooo  阅读(1587)  评论(0编辑  收藏  举报