在JS的開發過程中,大規模的JS腳本難以組織和維護,這一直是困擾前端開發人員的頭等問題。Extjs為了解決這種問題,在Extjs 4.x版本中引入了MVC開發模式,開始將一個JS(Extjs)應用程序分割成Model-View-Controller三層,為JS應用程序的如何組織代碼指明了方向,同時使得大規模JS代碼變得更加易於重用和維護;這就是Extjs MVC開發模式的初衷。
在官方給出的MVC例子中,我們可以看到一個簡單的列表編輯功能,這篇文章就圍繞這個功能進行詳細的講解,讓我們一起來揭開Extjs MVC的神秘面紗!
本文的示例代碼適用於Extjs 4.x和Extjs 5.x,在Extjs 4.2.1 和Extjs 5.0.1中親測可用!
常規開發模式下的列表編輯功能實現
我們先來看一下這個例子,它的功能非常簡單:在頁面打開的時候加載一個列表,當雙擊列表中一行數據的時候打開編輯窗口,編輯完成之后點擊保存按鈕,然后更新列表。截圖如下:
extjs-mvc-in-detail
在常規的開發模式下,要實現這個功能非常簡單,代碼如下:
Ext.onReady(function () { //1.定義Model Ext.define("MyApp.model.User", { extend: "Ext.data.Model", fields: [ { name: 'name', type: 'string' }, { name: 'age', type: 'int' }, { name: 'phone', type: 'string' } ] }); //2.創建store var store = Ext.create("Ext.data.Store", { model: "MyApp.model.User", data: [ { name: "Tom", age: 5, phone: "123456" }, { name: "Jerry", age: 3, phone: "654321" } ] }); //3.創建grid var viewport = Ext.create("Ext.container.Viewport", { layout: "fit", items: { xtype: "grid", model: "MyApp.model.User", store: store, columns: [ { text: '姓名', dataIndex: 'name' }, { text: '年齡', dataIndex: 'age', xtype: 'numbercolumn', format: '0' }, { text: '電話', dataIndex: 'phone' } ] } }); //4.添加雙擊編輯 var grid = viewport.down("grid"); grid.on("itemdblclick", function (me, record, item, index, e, eopts) { //5.創建編輯表單 var win = Ext.create("Ext.window.Window", { title: "編輯用戶", width: 300, height: 200, layout: "fit", items: { xtype: "form", margin: 5, border: false, fieldDefaults: { labelAlign: 'left', labelWidth: 60 }, items: [ { xtype: "textfield", name: "name", fieldLabel: "姓名" }, { xtype: "numberfield", name: "age", fieldLabel: "年齡" }, { xtype: "textfield", name: "phone", fieldLabel: "電話" } ] }, buttons: [ { text: "保存", handler: function () { win.down("form").updateRecord(); record.commit(); win.close(); } } ] }); win.down("form").loadRecord(record); win.show(); }); });
在這段代碼中,我們用到了Model、Store、Grid,以及編輯的Window和Form。代碼中已經給出了簡單的注釋,這不是今天重點要介紹的。
Extjs MVC開發模式
假設你從來沒有接觸過Extjs MVC開發模式,但是你知道ASP.NET MVC、或者別的任何語言下的MVC開發模式,那么我們來試想一下Extjs下的MVC該是什么樣子?
- JS是需要在html頁面中運行的,所以它要有一個宿主頁面
- 它要有Model、View和Controller三層,這是MVC的基礎,如果缺少了這三層,那還叫什么MVC呢?
- 這個JS程序可能需要有一個入口,因為JS不像ASP.NET那樣根據URL來分配Controller和Action
好了,我們暫時想到了這么多,那么在實際的Extjs MVC開發過程中是什么樣子呢?我們來看一下目錄結構:
第一步,創建入口頁面
創建一個html頁面,我們使用mvc.html頁面,在這個頁面的同一個目錄,我們創建一個app的文件夾,在這個文件夾下面用來放置js代碼。mvc.html就是我們的程序宿主頁面。
第二步,創建目錄結構
在app文件夾下面創建controller、model、store和view文件夾,從名稱上就知道他們該放置什么代碼了吧。然后創建Application.js作為我們程序的入口文件,並在mvc.html中引用Application.js文件。
第三步,創建model
在model文件夾下面,創建user.js文件:
這個文件將存放我們的model,我們可以直接把最上面定義model的代碼賦值到這里面。
app/model/User.js 的代碼如下:
Ext.define('MyApp.model.User', { extend: 'Ext.data.Model', fields: [ { name: 'name', type: 'string' }, { name: 'age', type: 'int' }, { name: 'phone', type: 'string' } ] });
第四步,創建store
雖然store不是mvc中必須的步驟,但是作為數據倉庫,store起到了數據存取的作用,grid、form等展現的數據都是通過store來提供的,因此store在extjs mvc開發模式中是必不可少的。
app/store/user.js 的代碼如下:
Ext.define("MyApp.store.User", { extend: "Ext.data.Store", model: "MyApp.model.User", data: [ { name: "Tom", age: 5, phone: "123456" }, { name: "Jerry", age: 3, phone: "654321" } ] });
第五步,創建view
為了實現列表和編輯功能,我們需要兩個視圖,分別是list和edit,那么view的結構如下:
app/view/user/List.js 對應我們的grid的定義,只不過將創建grid的實例改成了創建grid的擴展。
app/view/user/List.js 代碼如下:
Ext.define("MyApp.view.user.List", { extend: "Ext.grid.Panel", alias: 'widget.userlist', store: "User", initComponent: function () { this.columns = [ { text: '姓名', dataIndex: 'name' }, { text: '年齡', dataIndex: 'age', xtype: 'numbercolumn', format: '0' }, { text: '電話', dataIndex: 'phone' } ]; this.callParent(arguments); } });
app/view/user/edit.js 對應我們的window的定義,但同樣是通過擴展的形式來實現的。
app/view/user/edit.js 代碼如下:
Ext.define("MyApp.view.user.Edit", { extend: "Ext.window.Window", alias: "widget.useredit", title: "編輯用戶", width: 300, height: 200, layout: "fit", items: { xtype: "form", margin: 5, border: false, fieldDefaults: { labelAlign: 'left', labelWidth: 60 }, items: [ { xtype: "textfield", name: "name", fieldLabel: "姓名" }, { xtype: "numberfield", name: "age", fieldLabel: "年齡" }, { xtype: "textfield", name: "phone", fieldLabel: "電話" } ] }, buttons: [ { text: "保存", action: "save" } ] });
注意:對於view類的創建,我們需要定義alias,這是為了方便我們通過xtype來創建該組件的實例。(如果沒有alias,我們將很難在viewport和controller中使用 —— 這是我爬過的坑!)
第六步,創建controller
controller作為連接model、store和view的橋梁,在mvc開發模式中起到了至關重要的作用。如果說model定義了數據模式、store提供了數據存取的方法、view用來展示數據,那么controller將用來控制具體的數據操作。
app/controller/user.js 的代碼如下:
Ext.define('MyApp.controller.User', { extend: 'Ext.app.Controller', stores: ['User'], models: ['User'], views: ['Viewport', 'user.List', 'user.Edit'], init: function () { this.control({ 'userlist': { itemdblclick: this.editUser }, 'useredit button[action=save]': { click: this.saveUser } }); }, editUser: function (grid, record) { var win = Ext.widget("useredit"); win.down("form").loadRecord(record); win.show(); }, saveUser: function (btn) { var win = btn.up("window"), form = win.down("form"), record = form.getRecord(); form.updateRecord(); record.commit(); win.close(); } });
我們來詳細的說一下controller的這段代碼。在這段代碼中:
- 將定義好的model、store、view作為配置項添加到controller中,controller會加載這些文件;
- 在init方法中為view添加響應事件(這里添加事件的方法是通過query方式獲取控件並添加的,這也是為什么要為view添加alias的原因)
- editUser方法和saveUser方法則是具體的操作內容
有了這些步驟,我們就將model、store、view聯系在一起了。然后,我們進入Application.js文件中,完善我們的入口頁面。
第七步,完善Application.js文件
在很多時候,Application.js文件也被簡單的命名為app.js,它們的作用是一樣的,為應用程序提供一個入口。它可以很簡單,我們的Application.js文件代碼如下:
Ext.application({ name: "MyApp", appFolder: 'app', controllers: ["User"], autoCreateViewport: true, launch: function () { // 頁面加載完成之后執行 } });
- name: 應用程序名稱
- appFolder: 應用程序代碼的目錄,用來進行動態加載代碼的
- controllers: 應用程序使用到的控制器
- autoCreateViewport: 是否自動創建Viewport,默認為false,我們這里設置為true,當它被設置為true的時候,應用程序會自動創建Viewport,這個Viewport的定義在我們的app/view/viewport.js 中;如果為false的時候,我們需要在launch中手動創建相應的視圖。
第八步,Viewport.js的定義
Viewport作為我們應用程序的視圖面板,可以被單獨的定義在一個Viewport.js文件中。它的定義也很簡單,通常用來將一個或多個view作為它的子控件。
app/view/viewport.js 代碼如下:
Ext.define("MyApp.view.Viewport", { extend: "Ext.container.Viewport", layout: "fit", items: { xtype:"userlist" } });
完成這些步驟之后,我們可以運行mvc.html來查看效果。
總結
Extjs MVC開發模式為我們提供了一個完善的代碼組織和維護的方向,它的出發點是好的,但是在實際的操作過程中,我們會發現這種模式過於繁瑣,這可能是由於我們的示例太過於簡單而造成的。
Extjs MVC的Model、Store、View、Controller各層的代碼都是通過Ext.define來創建類的形式完成的,因此在使用Extjs MVC之前,我們需要對Extjs的類系統有一定的認識,包括如何使用Ext.define自定義類。
對於View層的控件,我們需要為它們指定一個alias屬性,方便通過xtype創建對象,並可以在Controller中方便的找到他,為它的子控件添加具體的操作。