【乾貨理解】理解javascript中實現MVC的原理

龍恩0707發表於2016-01-24

理解javascript中的MVC

MVC模式是軟體工程中一種軟體架構模式,一般把軟體模式分為三部分,模型(Model)+檢視(View)+控制器(Controller);

模型:模型用於封裝與應用程式的業務邏輯相關的資料以及對資料處理的方法。模型有對資料直接訪問的權利。模型不依賴 "檢視" 和 "控制器", 也就是說 模型它不關心頁面如何顯示及如何被操作.

檢視:檢視層最主要的是監聽模型層上的資料改變,並且實時的更新html頁面。當然也包括一些事件的註冊或者ajax請求操作(釋出事件),都是放在檢視層來完成。

控制器:控制器接收使用者的操作,最主要是訂閱檢視層的事件,然後呼叫模型或檢視去完成使用者的操作;比如:當頁面上觸發一個事件,控制器不輸出任何東西及對頁面做任何處理; 它只是接收請求並決定呼叫模型中的那個方法去處理請求, 然後再確定呼叫那個檢視中的方法來顯示返回的資料。

下面我們來實現一個簡單的下拉框控制元件,我們可以對它進行增刪操作;如下圖所示:

程式碼如下:

/*
 模型用於封裝與應用程式的業務邏輯相關的資料以及對資料處理的方法。模型有對資料直接訪問的權利。
 模型不依賴 "檢視" 和 "控制器", 也就是說 模型它不關心頁面如何顯示及如何被操作.
*/
function Mode(elems) {
    // 所有元素
    this._elems = elems;
    
    // 被選中元素的索引
    this._selectedIndex = -1;

    // 增加一項
    this.itemAdd = new Event(this);

    // 刪除一項
    this.itemRemoved = new Event(this);

    this.selectedIndexChanged = new Event(this);
}

Mode.prototype = {

    constructor: 'Mode',

    // 獲取所有的項
    getItems: function(){
        return [].concat(this._elems);
    },
    // 增加一項
    addItem: function(elem) {
        this._elems.push(elem);
        this.itemAdd.notify({elem:elem});
    },
    // 刪除一項
    removeItem: function(index) {
        var item = this._elems[index];
        this._elems.splice(index,1);
        this.itemRemoved.notify({elem:item});

        if(index === this._selectedIndex) {
            this.setSelectedIndex(-1);
        }
    },
    getSelectedIndex: function(){
        return this._selectedIndex;
    },
    setSelectedIndex: function(index){
        var previousIndex = this._selectedIndex;
        this._selectedIndex = index;
        this.selectedIndexChanged.notify({previous : previousIndex});
    }
};
/*
 下面是觀察者模式類,它又叫釋出---訂閱模式;它定義了物件間的一種一對多的關係,
  讓多個觀察者物件同時監聽某一個主題物件,當一個物件發生改變時,所有依賴於它的物件都將得到通知。
*/
function Event(observer) {
    this._observer = observer;
    this._listeners = [];
}
Event.prototype = {
    constaructor: 'Event',
    attach : function(listeners) {
        this._listeners.push(listeners);
    },
    notify: function(objs){
        for(var i = 0,ilen = this._listeners.length; i < ilen; i+=1) {
            this._listeners[i](this._observer,objs);
        }
    }
};

/*
 * 檢視顯示模型資料,並觸發UI事件。
 */
function View(model,elements){
    this._model = model;
    this._elements = elements;
    
    this.listModified = new Event(this);
    this.addButtonClicked = new Event(this);
    this.delButtonClicked = new Event(this);
    var that = this;

    // 繫結模型監聽器
    this._model.itemAdd.attach(function(){
        that.rebuildList();
    });
    this._model.itemRemoved.attach(function(){
        that.rebuildList();
    });

    // 將監聽器繫結到HTML控制元件上
    this._elements.list.change(function(e){
        that.listModified.notify({index: e.target.selectedIndex});
    });
    // 新增按鈕繫結事件
    this._elements.addButton.click(function(e){
        that.addButtonClicked.notify();
    });
    // 刪除按鈕繫結事件
    this._elements.delButton.click(function(e){
        that.delButtonClicked.notify();
    });
}
View.prototype = {
    constructor:  'View',
    show:  function(){
        this.rebuildList();
    },
    rebuildList: function(){
        var list = this._elements.list,
            items,
            key;
        list.html("");
        items = this._model.getItems();
        for(key in items) {
            if(items.hasOwnProperty(key)) {
                list.append('<option value="'+items[key]+'">' +items[key]+ '</option>');
            }
        }
        this._model.setSelectedIndex(-1);
    }
};
/*
 控制器響應使用者操作,呼叫模型上的變化函式
 負責轉發請求,對請求進行處理
*/
function Controller(model,view) {
    this._model = model;
    this._view = view;
    var that = this;

    this._view.listModified.attach(function(sender,args){
        that.updateSelected(args.index);
    });
    this._view.addButtonClicked.attach(function(){
        that.addItem();
    });
    this._view.delButtonClicked.attach(function(){
        that.delItem();
    });
}
Controller.prototype = {
    constructor: 'Controller',

    addItem: function(){
        var item = window.prompt('Add item:', '');
        if (item) {
            this._model.addItem(item);
        }
    },
    
    delItem: function(){
        var index = this._model.getSelectedIndex();
        if(index !== -1) {
            this._model.removeItem(index);
        }
    },
    
    updateSelected: function(index){
        this._model.setSelectedIndex(index);
    }
};

HTML程式碼如下:

<select id="list" size="10" style="width: 10rem"></select><br/>
<button id="plusBtn">  +  </button>
<button id="minusBtn">  -  </button>

頁面初始化程式碼如下:

$(function () {
    var model = new Mode(['PHP', 'JavaScript']),
      view = new View(model, {
        'list' : $('#list'), 
        'addButton' : $('#plusBtn'), 
        'delButton' : $('#minusBtn')
       }),
       controller = new Controller(model, view);        
        view.show();
}); 

程式碼分析如下:

  先分下下 我們是要實現什麼樣的功能; 基本功能有:一個下拉框,通過使用者輸入的操作來實現使用者增加一項及使用者選中一項後刪除一項的功能;
當然也新增了使用者切換到那一項的事件;

比如我們現在來增加一條資料的時候,在檢視層上新增監聽事件,如下程式碼:
// 新增按鈕繫結事件
this._elements.addButton.click(function(e){
    that.addButtonClicked.notify();
});
然後呼叫觀察者類Event中的方法notify(釋出一個事件) that.addButtonClicked.notify();大家都知道,觀察者模式又叫釋出-訂閱模式,
讓多個觀察者物件同時監聽某一個主題物件,當某一個主題物件發生改變的時候,所有依賴它的物件都會得到通知;
因此在控制層(Controller)我們可以使用如下程式碼對釋出者進行監聽操作:
this._view.addButtonClicked.attach(function(){
    that.addItem();
});
之後呼叫自身的方法addItem();程式碼如下:
addItem: function(){
    var item = window.prompt('Add item:', '');
    if (item) {
        this._model.addItem(item);
    }
}
呼叫模型層(model)的方法addItem();把一條資料插入到select框裡面去;model(模型層)的addItem()方法程式碼如下:
// 增加一項
addItem: function(elem) {
    this._elems.push(elem);
    this.itemAdd.notify({elem:elem});
},
如上程式碼 增加一項後,通過 this.itemAdd 釋出一個訊息,然後在檢視層(View)上通過如下程式碼來監聽這個訊息;程式碼如下:
// 繫結模型監聽器
this._model.itemAdd.attach(function(){
      that.rebuildList();
});
最後監聽到模型上(Model)的資料發生改變後,及時呼叫自身的方法rebuildList()去更新頁面上的資料;

模型層(Model)最主要做業務資料封裝操作。檢視層(View)主要釋出事件操作及監聽模型層上的資料,如果模型層上有資料改變的時候,及時更新頁面操作,
最後顯示給頁面上來,控制層(Controller)主要監聽檢視層(View)的事件,呼叫模型層(Model)的方法來更新模型上的資料,模型層資料更新後,會發布
一條訊息出去,最後檢視層(View)通過監聽模型層(Model)的資料變化,來更新頁面的顯示; 如上是MVC的基本流程。
MVC的優點:
1. 耦合性低:檢視層和業務層分離了,如果頁面上顯示改變的話,直接在檢視層更改即可,不用動模型層和控制層上的程式碼;也就是檢視層 與 模型層和控制層
已經分離了;所以很容易改變應用層的資料層和業務規則。
2. 可維護性:分離檢視層和業務邏輯層也使得WEB應用更易於維護和修改。
MVC的缺點:
個人覺得適合於大型專案,對於中小型專案並不適合,因為要實現一個簡單的增刪改操作,只需要一點點JS程式碼,但是MVC模式程式碼量明顯增加了。
對於學習成本也就提高了,當然如果使用一些封裝好的MVC庫或者框架就好了。

 

相關文章