MVC过程介绍
用户对View操作以后,View捕获到这个操作,会把处理的权利交移给Controller,Controller对相应的Model进行修改,当Model变更了以后,会通过观察者模式通知View进行更新。这里的关键步骤就是实现观察者模式。
实现过程
- 首先我们要明确我们的目标:实现数组的排序
<input type="button" value="reverse" id="toggleBtn"/>
<!--页面初始化后,input会显示数组的初始化值,当点击reverse按钮后,显示反转后的数组-->
<input type="text" data-bind="arr">
<!--用于测试多个dom结点绑定同一变量的情况-->
<input type="text" data-bind="arr">
new Controller(function (model){
var data = [1,2,3,4,5];
//调用set后,绑定了该数据的视图,会做相应的改变
model.arr.set(data);
var toggleBtn = document.getElementById('toggleBtn');
toggleBtn.addEventListener('click',function () {
//同样会自动更新视图
model.arr.set(data.reverse());
})
});
所以,这里关键的地方就是构造一个model对象,在model对象上事先已经绑定了所有的数据和其对应的视图,一旦某一个数据被改变,相应的视图也会进行更新。
2. 设计我们的MVC框架——mvc.js
定义一个观察者模式
function SubPub() {
this.value = "";
this.doms = [];
}
//类似于观察者中的publish
SubPub.prototype.set = function (value) {
var self = this;
selt.value = value;
//异步更新队列
setTimeout(function () {
self.doms.forEach(function (dom) {
dom.value = value.toString();
})
},0)
}
//类似于观察者中的subscribe
SubPub.prototype.bind = function (dom) {
this.doms.push(dom);
}
function Controller(callback) {
var doms = document.querySelectorAll('[data-bind]');
var model= {};
doms.forEach(function (dom) {
var data = dom.getAttribute('data-bind');
//可能多个dom结点绑定同一变量
model[data] = model[data] || new SubPub();
model[data].bind(dom);
})
callback.call(this,model);
}
使用ES5的新特性——Object.defineProperty
上面的代码中,我们修改数据时是这样的:
model.arr.set(data);
不是很自然,可不可换成这样呢:
model.arr = data;
实现这个功能就要用到Object.defineProperty中的set了,这样我们也不需要定义观察者了。
最后我们的mvc.js变成这样:
function Controller(callback) {
var model = {};
var doms = document.querySelectorAll('[data-bind]');
doms.forEach(function (dom) {
var data = dom.getAttribute('data-bind');
if(model.data===undefined){
model.data = null;
var targets = document.querySelectorAll('[data-bind=' + data + ']');
Object.defineProperty(model,data,{
set: function (value) {
model.data = value;
targets.forEach(function (target) {
target.value = value;
})
}
})
}
})
callback.call(this,model);
}