簡單實(shí)現(xiàn)前端MVC框架

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)系,都通過 Presenter 傳遞。
? View 非常薄,不部署任何業(yè)務(wù)邏輯,稱為"被動(dòng)視圖"(Passive View),即沒有任何主動(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è)過的回調(diào)函數(shù)
    // 按照J(rèn)avascript事件處理的一般機(jī)制,我們異步地調(diào)用回調(diào)函數(shù)
    // 如果覺得setTimeout影響性能,也可以采用requestAnimationFrame
    setTimeout(function() {
        self._listeners.forEach(function(listener) {
            listener.call(self, value);
        });
    });
};
Model.prototype.watch = function(listener) {
    // 注冊(cè)監(jiān)聽的回調(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)一步簡化
// 抽離中間方法
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);
});
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容