MVC/MVP/MVVM的區(qū)別
1. MVC
傳統(tǒng)的MVC主要分為三部分
? View 傳送指令到 Controller
? Controller 完成業(yè)務(wù)邏輯后存谎,要求 Model 改變狀態(tài)
? Model 將新的數(shù)據(jù)發(fā)送到 View,用戶得到反饋
MVC通用架構(gòu)
實(shí)際項(xiàng)目往往會(huì)更靈活贵白,比如Backbone 索性取消了 Controller高蜂,只保留一個(gè) Router(路由器) 。
2. MVP
MVP 模式將 Controller 改名為 Presenter,同時(shí)改變了通信方向。
? 各部分之間的通信媚狰,都是雙向的。
? View 與 Model 不發(fā)生聯(lián)系妄痪,都通過(guò) Presenter 傳遞哈雏。
? View 非常薄楞件,不部署任何業(yè)務(wù)邏輯衫生,稱為"被動(dòng)視圖"(Passive View),即沒(méi)有任何主動(dòng)性土浸,而 Presenter非常厚罪针,所有邏輯都部署在那里。
MVP架構(gòu)圖
3. MVVM
唯一的區(qū)別是黄伊,它采用雙向綁定(data-binding):View的變動(dòng)泪酱,自動(dòng)反映在 ViewModel,反之亦然
MVVM架構(gòu)圖
實(shí)現(xiàn)model
01 初步
// Model 核心代碼
function Model(value) {
this._value = typeof value === 'undefined' ? '' : value;
this._listeners = [];
}
Model.prototype.set = function(value) {
var self = this;
self._value = value;
// model中的值改變時(shí),應(yīng)通知注冊(cè)過(guò)的回調(diào)函數(shù)
// 按照J(rèn)avascript事件處理的一般機(jī)制墓阀,我們異步地調(diào)用回調(diào)函數(shù)
// 如果覺(jué)得setTimeout影響性能毡惜,也可以采用requestAnimationFrame
setTimeout(function() {
self._listeners.forEach(function(listener) {
listener.call(self, value);
});
});
};
Model.prototype.watch = function(listener) {
// 注冊(cè)監(jiān)聽(tīng)的回調(diào)函數(shù)
this._listeners.push(listener);
};
// html 代碼:
<div id="div1"></div>
// 邏輯代碼:
(function() {
var model = new Model();
var div1 = document.getElementById('div1');
model.watch(function(value) {
div1.innerHTML = value;
});
model.set('hello, this is a div');
})();
02 抽離中間方法,進(jìn)一步簡(jiǎn)化
// 抽離中間方法
Model.prototype.bind = function(node) {
// 將watch的邏輯和通用的回調(diào)函數(shù)放到這里
this.watch(function(value) {
node.innerHTML = value;
});
};
// html代碼:
<div id="div1"></div>
<div id="div2"></div>
// 邏輯代碼:
(function() {
var model = new Model();
model.bind(document.getElementById('div1'));
model.bind(document.getElementById('div2'));
model.set('this is a div');
})();
實(shí)現(xiàn)controller
function Controller(callback) {
var models = {};
// 找到所有有bind屬性的元素
var views = document.querySelectorAll('[data-bind]');
// 將views處理為普通數(shù)組
views = Array.prototype.slice.call(views, 0);
views.forEach(function(view) {
var modelName = view.getAttribute('data-bind');
// 取出或新建該元素所綁定的model
models[modelName] = models[modelName] || new Model();
// 完成該元素和指定model的綁定
models[modelName].bind(view);
});
// 調(diào)用controller的具體邏輯斯撮,將models傳入经伙,方便業(yè)務(wù)處理
callback.call(this, models);
}
// html:
<div id="div1" data-bind="model1"></div>
<div id="div2" data-bind="model1"></div>
// 邏輯代碼:
new Controller(function (models) {
var model1 = models.model1;
model1.set('this is a div');
});
MVC完整版
function Model(value) {
this._value = typeof value === 'undefined' ? '' : value;
this._listeners = [];
}
Model.prototype.set = function(value) {
var self = this;
self._value = value;
setTimeout(function() {
self._listeners.forEach(function(listener) {
listener.call(self, value);
});
});
};
Model.prototype.watch = function(listener) {
this._listeners.push(listener);
};
Model.prototype.bind = function(node) {
this.watch(function(value) {
node.innerHTML = value;
});
};
function Controller(callback) {
var models = {};
var views = Array.prototype.slice.call(document.querySelectorAll('[data-bind]'), 0);
views.forEach(function(view) {
var modelName = view.getAttribute('data-bind');
(models[modelName] = models[modelName] || new Model()).bind(view);
});
callback.call(this, models);
}
// html:
<span data-bind="hour"></span> : <span data-bind="minute"></span> : <span bind="second"></span>
// controller:
new Controller(function (models) {
function setTime() {
var date = new Date();
models.hour.set(date.getHours());
models.minute.set(date.getMinutes());
models.second.set(date.getSeconds());
}
setTime();
setInterval(setTime, 1000);
});