Thế nào là MVC Framework ? Điểm khác biệt của nó so với React

MVC Framework

MVC Framework trong javascript là một framework cho JS sử dụng mô hình MVC. Vậy mô hình MVC là gì

Như bạn đã biết, tên của mô hình MVC được dựa trên từng thành phần chính của nó: Model: chứa các model của dữ liệu ứng dụng, View sẽ render Model cho một đại diện thích hợp, và Controller sẽ cập nhật cho Model

Chúng ta định nghĩa kiến trúc đặc trưng của mô hình MVC như sau

  • Model: Các đại diện cụ thể của các thông tin mà trên đó các ứng dụng hoạt động. Nó sẽ định nghĩa thêm ý nghĩa cho các dữ liệu thô (ví dụ như tính toán nếu ngày hôm nay là sinh nhật của user, hoặc tính toán t, phí shipping của một đơn hàng vv...)
  • View: Render các model thành một hình thức thích hợp cho sự tương tác, thường là một yếu tố giao diện người dùng. MVC là thường thấy trong các ứng dụng web, nơi mà view là các trang HTML và code sẽ đưa các dữ liệu động cho trang
  • Controller: Xử lý và đáp ứng các sự kiện, như là các hành động của users, gọi những thay đổi của model và view

Trên đây là những lý thuyết tôi cóp nhặt từ trên mạng, bạn chả hiểu gì đúng không.

Bây giờ chúng ta sẽ thử áp dụng mô hình MVC trong khi phát triển một component của JS

Chúng ta có một List box HTML (select tag) có thể chỉnh sửa danh sách các item. Người sử dụng có thể lựa chọn, loại bỏ và thêm các mục mới vào danh sách

Model của dữ liệu sẽ như sau: là một mảng các item, có thể chọn và xóa

function ListModel(items) {
    this._items = items;
    this._selectedIndex = -1;

    // Các item sẽ có event thêm/xóa và selected
    this.itemAdded = new Event(this);
    this.itemRemoved = new Event(this);
    this.selectedIndexChanged = new Event(this);
}
//Định nghĩa các function của model

ListModel.prototype = {
    getItems : function () {
        return [].concat(this._items);
    },

    addItem : function (item) {
        this._items.push(item);
        this.itemAdded.notify({ item : item });
    },

    removeItemAt : function (index) {
        var item;

        item = this._items[index];
        this._items.splice(index, 1);
        this.itemRemoved.notify({ item : item });
        if (index === this._selectedIndex) {
            this.setSelectedIndex(-1);
        }
    },

    getSelectedIndex : function () {
        return this._selectedIndex;
    },

    setSelectedIndex : function (index) {
        var previousIndex;

        previousIndex = this._selectedIndex;
        this._selectedIndex = index;
        this.selectedIndexChanged.notify({ previous : previousIndex });
    }
};

Event là một class để quan sát việc thay đổi danh sách items

function Event(sender) {
    this._sender = sender;
    this._listeners = [];
}

Event.prototype = {
    attach : function (listener) {
        this._listeners.push(listener);
    },
    notify : function (args) {
        var index;

        for (index = 0; index < this._listeners.length; index += 1) {
            this._listeners[index](this._sender, args);
        }
    }
};

Class của View cần định nghĩa dạng tương tác với form. Chúng ta sẽ làm một list item (select tag), bên cạnh có 2 nút "+" và "-" để điều khiển việc thêm (xóa) item

View Class:

function ListView(model, elements) {
    this._model = model;
    this._elements = elements;

    this.listModified = new Event(this);
    this.addButtonClicked = new Event(this);
    this.delButtonClicked = new Event(this);

    var _this = this;

    // Gắn sự kiện vào model
    this._model.itemAdded.attach(function () {
        _this.rebuildList();
    });
    this._model.itemRemoved.attach(function () {
        _this.rebuildList();
    });

    // Gắn listeners vào HTML control
    this._elements.list.change(function (e) {
        _this.listModified.notify({ index : e.target.selectedIndex });
    });
    this._elements.addButton.click(function () {
        _this.addButtonClicked.notify();
    });
    this._elements.delButton.click(function () {
        _this.delButtonClicked.notify();
    });
}

ListView.prototype = {
    show : function () {
        this.rebuildList();
    },

    rebuildList : function () {
        var list, items, key;

        list = this._elements.list;
        list.html('');

        items = this._model.getItems();
        for (key in items) {
            if (items.hasOwnProperty(key)) {
                list.append($('<option>' + items[key] + '</option>'));
            }
        }
        this._model.setSelectedIndex(-1);
    }
};

Controller sẽ thực hiện việc thêm/xóa và cập nhật danh sách. Chúng ta cũng sẽ có 3 function tương ứng

function ListController(model, view) {
    this._model = model;
    this._view = view;

    var _this = this;

    this._view.listModified.attach(function (sender, args) {
        // thực hiện việc cập nhật list items
        _this.updateSelected(args.index);
    });

    this._view.addButtonClicked.attach(function () {
        // thực hiện việc thêm item
        _this.addItem();
    });

    this._view.delButtonClicked.attach(function () {
        // xóa item
        _this.delItem();
    });
}

ListController.prototype = {
    addItem : function () {
        var item = window.prompt('Add item:', '');
        if (item) {
            this._model.addItem(item);
        }
    },

    delItem : function () {
        var index;

        index = this._model.getSelectedIndex();
        if (index !== -1) {
            this._model.removeItemAt(this._model.getSelectedIndex());
        }
    },

    updateSelected : function (index) {
        this._model.setSelectedIndex(index);
    }
};

Vậy là chúng ta đã xây dựng xong 3 thành phần của MVC. Dĩ nhiên chúng phải được kết hợp với nhau để code hoàn chỉnh

  • Khởi tạo giá trị ban đầu cho Model
  • Tạo View
  • Tạo Controller
$(function () {
    var model = new ListModel(['Ruby', 'JavaScript']),
        view = new ListView(model, {
            'list' : $('#list'),
            'addButton' : $('#plusBtn'),
            'delButton' : $('#minusBtn')
        }),
        controller = new ListController(model, view);

    view.show();
});

Việc còn lại chỉ là tạo HTML page

<select id="list" size="10"></select>
<button id="plusBtn">  +  </button>
<button id="minusBtn">  -  </button>

Trên đây chúng ta đã thử làm JS với mô hình MVC, trên thực tế có rất nhiều các Framework hỗ trợ chúng ta làm những việc trên đây như

  • AngularJS
  • EmberJS
  • MeteorJS
  • BackboneJS
  • KnockOutJS

Điểm khác biệt của MVC Framework và React

Như tôi đã trình bày, các MVC Framework cung cấp cả 3 phần của mô hình MVC: Model-View-Controller.

Còn React chỉ là một thư viện phát triển UI của Facebook để tạo ra các components tương tác, bạn có thể coi nó cung cấp phần "V" trong mô hình MVC

Như vậy bạn có thể sử dụng React bên trong các MVC framework một cách thoải mái. Ví dụ bạn có thể dùng React như các Directive bên trong AngularJS, Components trong Ember ...

Sau đây chúng ta thử dùng React để làm lại Demo bên trên

Chúng ta chỉ phải tạo class SelectBox, nó được khởi tạo các giá trị ban đầu, có 3 function là "thêm", "xóa" item và render lại view

var SelectBox = React.createClass({
    getInitialState: function() {
        return {
            Options: [
                {
                    value: "",
                    text: "Ruby"
                },
                {
                    value: "",
                    text: "JavaScript"
                }
            ]
        };
    },
    _onClickAddButtonHandler: function() {
        var itemName = prompt("Enter item name", ""),
            Options = this.state.Options;

        if (itemName.trim()) {
            Options.push({value: '', text: itemName});
            this.setState({
                Options: Options
            });
        }
    },
    _onClickRemoveButtonHandler: function() {
        var selectedIndex = this.refs.select_box.getDOMNode().selectedIndex,
            Options = this.state.Options;

        if (selectedIndex >= 0) {
            Options.splice(selectedIndex, 1);

            this.setState({
                Options: Options
            });
        }
    },
    render: function() {
        var Options = this.state.Options;

        var OptionElements = Options.map(function(item, idx) {
            return (
                <option value={item.value}>{item.text}</option>
            );
        });

        return (
            <div>
                <select size="20" ref="select_box">
                    {OptionElements}
                </select>
                <button onClick={this._onClickAddButtonHandler}>+</button>
                <button onClick={this._onClickRemoveButtonHandler}>-</button>
            </div>
        );
    }
});

Và việc cuối cùng chỉ cần làm là render cái SelectBox đó ra view

React.render(<SelectBox />, document.body);

Rất gọn gàng và đơn giản phải không nào.

Còn bạn đã biết AngularJS, bạn hãy thử kết hợp nó và React xem sao 😃