深入學習前端MVC和MVVM(二)

上一節說了後臺的MVC,現在開始講重點,前端的MVC又是一個什麼鬼。
很長一段時間我都沒有搞清楚MVC和MVVM。
一直在說ng是MVC,react和Vue是MVVM,MVVM我用過了,用過vue和react,他們的數據綁定,那麼MVC究竟是什麼樣子呢?

一個簡單MVC的實現

有一篇很好的文章講了MVC:
http://www.imooc.com/article/4911

重點:
MVC的基礎是觀察者模式;
這裏還有一個問題,就是MVC爲什麼不是23種設計模式中的一種呢?
在網上找了很多答案:
說MVC每一個M、V、C都是一種模式,即觀察者模式、策略模式、組合模式的結合;

第一步:先實現model來看看

這裏我們先看Model.js

//實際上這裏是一個觀察者模式

function Model(value) {
    this._value = typeof value === 'undefined' ? '' : value;
    this._listeners = [];
}

Model.prototype.set = function (value) {
    var self = this;
    self._value = value;
    console.log(value);
    // model中的值改變時,應通知註冊過的回調函數
    // 按照Javascript事件處理的一般機制,我們異步地調用回調函數
    // 如果覺得setTimeout影響性能,也可以採用requestAnimationFrame
    setTimeout(function () {
        self._listeners.forEach(function (listener) {
            listener.call(self, value);
        });
    });
};
Model.prototype.watch = function (listener) {
    // 註冊監聽的回調函數
    console.log(listener);
    this._listeners.push(listener);
};
<div id="div1"></div>
<script src="./model.js"></script>
<script>
// 邏輯代碼:
(function() {
    var model = new Model();
    var div1 = document.getElementById('div1');
    var setValue=function (value) {
        div1.innerHTML=value;
    }
    // model.watch(function(value) {
    //     div1.innerHTML = value;
    // });
    model.watch(setValue)
    model.set('hello, this is a div');
    model.set('hello, this is a div2');

})();
</script>

上面代碼的調用是這樣的:提供了一個監聽方法,Model.prototype.wacth,在實例中,監聽的就是setValue這個方法,(我把原來代碼裏面的匿名函數註釋掉了,因爲這樣便於理解)
監聽事件列隊插入 listener;
這裏事實上我們並沒有執行直接通過setValue(‘hello, this is a div’)執行,而是調用model的set方法執行的setValue(‘hello, this is a div’)

在model的set方法中,是遍歷監聽的事件。並將傳給set的值,傳給listener中的每一個事件。

比如我們稍微改一下:

/*view*/
<div id="div1"></div>
<div id="div2"></div>

<script src="./model.js"></script>
<script>
// 邏輯代碼:
(function() {
    var model = new Model();
    var div1 = document.getElementById('div1');
    var div2 = document.getElementById('div2');

    var setValue=function (value) {
        div1.innerHTML=value;
    }
    var setValue2=function (value) {
        div2.innerHTML=value;
    }

    model.watch(setValue)
    model.watch(setValue2)
    model.set('hello, this is a div2');

})();
</script>

上面的代碼model監聽了兩個事件,爲div1和div2設值的setValue()和setValue2()
這裏只調用了一次set方法,但是這兩個setValue都執行了。

藉助觀察者模式,我們已經實現了在調用model的set方法改變其值的時候,模板也同步更新,但這樣的實現卻很彆扭,因爲我們需要手動監聽model值的改變(通過watch方法)並傳入一個回調函數,有沒有辦法讓view(一個或多個dom node)和model更簡單的綁定呢?

進一步,改進model,分離model和view的業務

上面的代碼中view層還是有寫setValue函數,不符合view的邏輯,view應該是看不到內部的操作。因此通過bind的方式實現setValue

 var setValue=function (value) {
        div1.innerHTML=value;
    }

通過bind的方式實現,在model.js中加上

Model.prototype.bind = function (node) {
    // 將watch的邏輯和通用的回調函數放到這裏
    this.watch(function (value) {
        node.innerHTML = value;
    });
};

這個時候view的寫法:
就只是看到節點綁定到了model上,以及設置節點的值。

(function () {
    var model = new Model();
    model.bind(document.getElementById('div1'));
    model.bind(document.getElementById('div2'));
    model.set('this is a div');
})();

最後一步:controller

這裏一直只提到了MV,並沒有C,其實controller是一種看不見的方式在運作

這裏是controller.js的代碼:

function Controller(callback) {
    var models = {};
    // 找到所有有bind屬性的元素
    var views = document.querySelectorAll('[bind]');
    // 將views處理爲普通數組
    views = Array.prototype.slice.call(views, 0);
    views.forEach(function(view) {
        var modelName = view.getAttribute('bind');
        // 取出或新建該元素所綁定的model
        models[modelName] = models[modelName] || new Model();
        // 完成該元素和指定model的綁定
        models[modelName].bind(view);
    });
    // 調用controller的具體邏輯,將models傳入,方便業務處理
    callback.call(this, models);
}

view層,實際上controller層完成了view和model的綁定。

<div id="div1" view="model1"></div>
<div id="div2" view="model2"></div>

new Controller(function (models) {
    console.log(models);
    var model1 = models.model1;
    model1.set('this is a div');
});
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章