運用 Ext JS 4 的 MVC 架構


http://www.open-open.com/lib/view/open1350386395227.htmljavascript

http://www.ibm.com/developerworks/cn/web/1210_wangzh_extjsmvc/index.htmlcss


Ext JS 4 簡介html

Ext JS 4 目前是 Sencha 的產品,4.x 的正式版本號是 4.0.7。Ext JS 4 提供商業版本,但若是您的項目是開源的,則能夠無償使用 Ext JS 4。Ext JS 的論壇目前很是活躍;Ext JS 還在不但地升級改進,據 Sencha 官方統計,使用 Ext JS 的開發者數目在一百萬以上。前端

Ext JS 4 與以前版本的比較java

  • 渲染效率提升程序員

    全部類都通過調優,包括最影響渲染效率的佈局引擎重寫。web

  • 命名空間算法

    命名空間是 Ext JS 4 的 MVC 的基礎,自此 Ext JS 類能按做用域分開存放了。.NET 或 Java 開發者應該熟悉命名空間帶來的好處:命名空間讓全類名映射到類文件路徑變得很容易,將類按做用域分文件夾存放使得類更容易管理。以 MVC 爲例,Ext JS 類將按做用域:模型、視圖和控制器分爲三類,分別存放於對應文件夾中。數據庫

  • 按需加載類設計模式



    清單 1. 按需加載類的例子

    1 Ext.define('MyNamespace.Cat', {
    2      requires: ['MyNamespace.BabyCat'],
    3      giveBirth: function() {
    4          // 實例化 BabyCat 以前,必須加載 BabyCat 的類定義。經過設置「requires」屬性,能實現類的按需加載
    5          return new MyNamespace.BabyCat();
    6      }
    7  });

    這 個特性實際上是基於全新設計的類系統的,詳見下面的小結。不一樣於先前版本:即便用到 Ext JS 框架中不多一部分單元,Ext JS 也會加載全部的框架,按需加載只加載須要的類。所以按需加載類爲 JS 優化和減小內存消耗提供了一個有效途徑。Sencha 爲此還提供了 SDK 工具對 JS 代碼進行 Minify,在部署前運行 minify 對 JS 代碼最小化後,將獲得一個最小 JS 集合。

  • 全新的類系統 因爲篇幅的限制,具體請參閱官方文檔 Class System,其中詳細描述了怎樣用 Ext JS 4 的方式定義類,以及錯誤處理和調試

  • MVC 架構 用 Ext JS 4 以前的版本寫大的客戶端應用,您會發現愈來愈「難」,您會發現有四難:難寫、難讀、難維護、難擴展。隨着愈來愈多的功能添加進來,代碼愈來愈失控,一個 JS 文件幾千行可能很廣泛了,固然也不排除代碼組織得很好易於擴展的狀況,但這些都須要開發者付出額外的開發代價去組織本身的架構。從 Ext JS 4 開始有了本身的 MVC 架構,開發者沒必要再付出這種額外的代價也能寫出漂亮的代碼。Ext JS 4 對 MVC 有本身的定義,如下定義來自 Sencha 官網的文檔:

    • Model

      :一組字段的集合以及它們對應的數據(例如:「User」類 model 有「username」和「password」字段),經過 data 包 (store,proxy 等 )Model 能序列化本身,並能經過關聯關係從一個 Model 導航到另外一個 Model。Model 的工做原理相似 Ext JS 3 中的 Record 類,一般結合 Store 爲表格控件或其它控件提供顯示數據。

    • View

      :任意組件,如 Grid, Tree 和 Panel 都是視圖。

    • Controllers

      :在這裏寫全部的邏輯代碼:如渲染視圖、實例化模型、加載並初始化其它控制器等。

MVC 的概念很簡單,但實際項目中運用 MVC 模式將代碼組織起來會不會沒那麼簡單?答案在後面的章節「介紹開發 Ext JS4 的利器 : Sencha Architect 2」中,該章節會詳細介紹怎樣用該工具開發 MVC 模式的 Ext JS 程序。

爲何要運用 MVC 架構

MVC 的概念
MVC 是一種成熟的軟件設計模式。MVC 模式的目的是實現一種動態的程序設計,使後續對程序的修改和擴展簡化,而且使程序某一部分的重複利用成爲可能。除此以外,此模式經過對複雜度的簡化,使程 序結構更加直觀。軟件系統經過對自身基本部份分離的同時也賦予了各個基本部分應有的功能。專業人員能夠經過自身的專長分組:

  • 控制器 Controller - 負責轉發請求,對請求進行處理。

  • 視圖 View - 界面設計人員進行圖形界面設計。

  • 模型 Model - 程序員編寫程序應有的功能(實現算法等等)、數據庫專家進行數據管理和數據庫設計 ( 能夠實現具體的功能 )。

MVC 模式的特色:

  • 重用性

    Ext JS 4 中,數據模型、視圖組件和存儲組件能重複使用,使用時只需將它們的名稱做爲引用添加到特定的 controller 中。

  • 職能分離

    模型、視圖、控制器以及存儲之間的職能分離,使得每一個 JS 文件的職能單一化、最小化。開發人員只須要引用這些必需的職能單元便可構建新的功能。

  • 職責清晰

    因爲每個 JS 單元文件的職責清晰,不一樣類型的「職責」被劃分爲不一樣的組件。在集成開發工具中,開發人員很容易利用這些組件構建本身的應用。

  • 複雜性

    運用 MVC 模式時有必定的複雜性,由於開發者須要付出額外的努力去學習 Ext JS 4 的 MVC 框架,但在大型項目中這個付出是值得的。另外 Sencha 提供了相應的集成開發工具 Sencha Architect 協助基於 MVC 框架的開發,必定程度上減輕了因運用 MVC 模式帶給開發者的壓力。

運用 MVC 之前:

  • 代碼組織凌亂

    上面的小結中提到 Ext JS 4 以前的代碼一般會碰到四「難」,除此以外,代碼很難管理,因爲沒有分離出相似控制器單元,致使「拷貝、粘貼」過多,應用邏輯代碼支離破碎,給調試和測試帶來不少不便。

  • 缺少總體邏輯

    因爲視圖等「靜態」代碼和事件監聽代碼混雜在一塊兒,代碼功能職責混亂,不少本應做爲公用的代碼也一塊兒被混進去,致使可擴展性幾乎全無,所以爲新功能編寫新的代碼也變得愈來愈不容易,修改代碼更是容易出錯。

運用 MVC 之後:

  • 代碼層次結構清晰

    圖 1. 文件結構
    運用 Ext JS 4 的 MVC 架構



    清單 2. 入口文件

    01 <html>
    02  <head>
    03     <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    04     <title>Demo</title>
    05     <link rel="stylesheet" type="text/css"
    06      href="http://extjs.cachefly.net/ext-4.0.2a/resources/css/ext-all.css"/>
    07     <script type="text/javascript"
    08      src="http://extjs.cachefly.net/ext-4.0.2a/ext-all-debug.js"></script>
    09     <link rel="stylesheet" type="text/css" href="style.css"/>
    10     <script type="text/javascript" src="demo.js"></script>
    11 </head>
    12  <body></body>
    13 </html>

    整個工程僅此一個 HTML 文件 , 供十一行,其中 demo.js 是工程的啓動文件,裏面包含了全部 UI widget 的構建,後面會具體介紹。

    • JavaScript 腳本都按職責存放在不一樣的 JS 文件中。

    • 不一樣的 JS 文件按 MVC 目錄命名約定"controller"、"store"、"view"、"model"分類存放。

    • 整個工程裏只有一個 HTML 文件,並且只做爲入口文件,裏面不寫任何腳本。請看下面入口文件的例子。

  • 總體邏輯更加條理

    入口定義在 Ext.application({...}) 中,這是 Ext JS 4 爲開發 MVC 應用新增的函數,咱們能夠在裏面引入初始時所需的 controller、view 或 model,在 launch 函數中編寫初始代碼,和之前在 Ext.onReady() 中同樣。由於 Ext 引擎會在 Ext.application({...}) 函數執行時建立一個全局 application 實例並能在 controller 中被引用獲得 (this.application.*),因此咱們也能夠在 application 中定義一些公共函數,甚至註冊一些事件,方便其它 controller 調用。

    "model"文件夾中只有模型相關的類定義,每一個模型定義必須包含字段,還能定義字段校驗規則,不一樣模型之間的關聯,以及數據代理 ( 鏈接服務器存取數據 )。好比僱員\部門對應兩類模型,體現爲兩個類文件"Employee"和"Department"。

    "view"文件夾中定義了全部的 widget,每個 widget 對應一個類文件。view 的代碼屬於靜態代碼,後面將提到怎樣用工具自動生成。

    "controller"文件夾中的控制器按管理範圍的不一樣劃分爲不一樣的類文件,其中每個控制邏輯都包括初始化、組件事件監聽以及引 用等。好比,導航菜單的控制器會包含對主頁面菜單按鈕的動做監聽」click「,並在該事件中負責建立相應的子頁面;而子頁面對應的控制器只負責該頁面中 的組件的行爲,如提交按鈕的點擊等。另外控制器自己能被動態加載,在下文的例子中咱們還能夠看到,不一樣的控制器被加載的時機和順序是由用戶行爲(如點擊某 按鈕)控制的。其實這個特性是基於依賴於 Ext JS 4 的動態加載類的新特性的。詳見"按需加載類"一節。

    因而可知,運用 MVC 後 HTML 裏再也不直接寫 JS 了,JS 按邏輯、職責分門別類存於不一樣的目錄,對應到不一樣的文件中。

開發 Ext JS4 的利器:Sencha Architect

Sencha Architect 是 Sencha 公司出品的一款輔助 ExtJS 開發的商業 IDE 軟件,能幫助 ExtJS 開發人員更加專一於核心 JS 代碼的開發,從而大大減小花費在編寫界面、組織代碼等反覆性的工做上的時間。筆者寫做時的工具版本是 2.0.0 Build 412,此工具最大的特色是能幫助用戶管理符合 MVC 模式的代碼。例如,視圖類代碼能經過拖放組件結合屬性設置的方式徹底自動生成,不用寫手一行代碼。其它如 Model、Store 和 Controller 的代碼能經過屬性設置,方法、事件設置自動生成。理想狀況下,一個熟練的 ExtJS 開發者在使用 Sencha Architect 時,百分之九十以上的時間會花在 controller 的實現和自定義組件(包括 override 一些組件)的開發。這一點也不誇張,由於在 Sencha Architect 中開發界面實在過輕鬆了。感興趣的朋友能夠從官網下載 Sencha Architect 的 30 天試用版嚐嚐鮮。
主要特性有 :

  • 自動生成 view 代碼

    經過拖放組件的方式生成複雜視圖。



    圖 2. 視圖設計
    運用 Ext JS 4 的 MVC 架構

  • 代碼模式和設計模式切換

    設計好的視圖,能方便切換到代碼模式下預覽,拷貝或導出。



    圖 3. 模式切換
    運用 Ext JS 4 的 MVC 架構

  • 屬性設置面板

    圖 4. 屬性面板
    運用 Ext JS 4 的 MVC 架構

  • 項目導航面板

    圖 5. 導航面板
    運用 Ext JS 4 的 MVC 架構

示例和代碼



下面的示例程序是一個很是實用的集表單提交,表格應用和圖表顯示的綜合運用的例子。因爲篇幅限制,本文只列舉主要的 JS 單元,感興趣的朋友請到本文末尾處下載完整的示例程序。

  • 入口代碼

    本示例使用了默認的 appFolder:app, 實際中用戶能夠覆蓋此屬性,使用符合項目要求的路徑名。我在項目中傾向於 appFolder 中的全部類由 Sencha Architect 工具維護,經過配置 mvn,在 compile 時將 appFolder 中生成的類拷貝到 webApp 中。手工維護的 JS 文件放在獨立的命名空間中(稱其爲擴展空間吧),並在入口中聲明,這樣能被 application 引用並加載,同時在擴展空間的類也能 require 到 application 對應命名空間中的類,這樣作的好處是,您能將 override 的代碼移出來放到擴展空間中,另外還能放一些項目中用到的插件。


    清單 3. 入口代碼

    01 /* 動態加載依賴的前提 */
    02 Ext.Loader.setConfig({
    03    enabled: true,  
    04    paths: {
    05        'Extention': 'js'// 設置一個擴展命名空間,區分工具生成的代碼
    06    }
    07 });
    08
    09 Ext.application({
    10    requires: [
    11        'Extention.RandomGen'// 加載擴展命名空間中的類
    12    ],
    13
    14    views: [
    15        'MyViewport'
    16    ],
    17    autoCreateViewport: true,
    18    name: 'MyApp',
    19    controllers: [
    20     // 引用初始頁面時,所須要的最小 controller 集合,
    21     // 其它的由主菜單按鈕觸發動態加載
    22        'AppLaunchCtrl'
    23    ],
    24    /* 如下函數僅示意用戶能在 application 中定義本身的全局函數,具體實現請下載代碼後查看 */
    25    findTab: function(tabPanel,  record) {},
    26    activateTab: function(tabPanel, targetTab) {},
    27    widget: function(tabPanel, controllerName, widgetName, record, cfg) {}
    28 });


  • 主菜單控制器

    控制主菜單中三個按鈕的點擊事件,以及它們的狀態:如按下和浮起。

  • 表單子頁面控制器

    表單提交相關,本示例中,點擊提交按鈕後將彈出一個「Save」提示框。

  • 表格子頁面控制器

    經過查詢按鈕控制表格的 store 從新加載新數據。

  • 圖表子頁面控制器

    經過下拉選擇框控制圖表的刷新。



    清單 4. 圖表子頁面控制器代碼

     Ext.define('MyApp.controller.ChartCtrl', { 
        extend: 'Ext.app.Controller', 
    
        stores: [ 
            'MyChartStore', 
            'LatestMonths'
        ], 
    
        refs: [ 
            { 
                ref: 'tabPanel', 
                selector: '#tabPanel'
            }, 
            { 
                ref: 'latestMon', 
                selector: '#comboLatest'
            } 
        ], 
        /* 請參看 init 中的事件綁定 */ 
        onComboboxSelect: function(combo, records, options) { 
            console.log('onComboboxSelect'); 
            this.getMyChartStoreStore().load(); 
            return false; 
        }, 
    
        init: function(application) { 
    	    // 綁定月份下拉框的 select 事件
            this.control({ 
                "#comboLatest": { 
                    select: this.onComboboxSelect 
                } 
            }); 
    		
     /* 綁定 store 的 load 事件,完成動態數據效果(演示)
               實際中項目不須要這樣,
       應在 store 上定義 proxy 從服務器拿數據
     */ 
            this.getMyChartStoreStore().on( 
            { 
                'load' : function(me, operation, eOpts) { 
                    me.loadData(generateData(8)); 
                } 
            } 
            ); 
     // 初始化月份下拉框的值爲」1「
            this.getLatestMon().setValue('1'); 
     // 爲圖表加載數據,此處顯示調用,
     // 是由於此 store 屬性 autoLoad 定義 false 
            this.getMyChartStoreStore().load(); 
        } 
     }); 
     


  • 運行結果

    圖 6. 表單
    運用 Ext JS 4 的 MVC 架構



    圖 7. 表格
    運用 Ext JS 4 的 MVC 架構



    圖 8. 圖表
    運用 Ext JS 4 的 MVC 架構

項目中的主要問題和解決辦法

  • 關於 Sencha Architect 代碼的管理

    • 此工具能生成幾乎全部必須的代碼,但對於手工編寫的部分 JS 代碼,本文推薦在 webApp 下創建獨立的目錄,並在入口文件中定義對應的路徑到命名空間的映射。

    • 用編譯工具如 mvn 將生成目錄拷貝到 webApp 中。

  • 關於 Controller

    • 劃分好 controller 的範圍和生命週期。

      劃分好頁面功能區,而後肯定須要哪些 controller; 區分哪些是動態的,好比一個子窗口或新頁籤,對應 controller 的加載時機也應該是動態的。

    • 將須要動態加載的 Controller 從入口文件定義中移除。

      這樣作的一個顯而易見的好處是,避免初始頁面"過載";另外能避免因 init()調用太早致使事件綁定失敗,緣由也顯而易見:須要綁定事件的組件尚未被建立出來。

  • 關於 store 多實例

    設置過 storeId 的 store 將在 storeManager 中註冊爲單例;有時這是個限制,好比想在多個 view 實例中擁有獨立的 store 時,Sencha Architect 目前沒有好的辦法。辦法有二,一是 override view,讓每一個 view 在 create 時擁有本身的 store;二是在 Model 中設置 proxy( 固然這時得移除 store 中的 proxy)。第二種方法來自 Sencha 的技術支持,但我還沒試過。

  • 關於 TreePanel 對屬性設置的要求 我在使用 Sencha Architect 的時候碰到過這個問題,Architect 能顯示熱數據,即屬性設置好後,設計器能實時將數據做爲預覽顯示出來;我一開始就碰到樹不能顯示的問題,接着是顯示了又沒法展開下級。

    • 設置好 store 的 proxy 中的 idProperty、root 屬性。

      我一開始設置錯了 root(設置了一個不存在的 field),後來查了好久,找到了這個位置 , 問題解決。中間還懷疑是工具的 bug :-)。 瞧,一個很不起眼的地方,但很打擊士氣。

    • 設置好樹節點所用到的 Model 中的 idProperty 屬性。

      這個屬性決定點擊樹的節點往下鑽取時往服務器傳遞的參數。



總結

Ext JS 從 4.0 之後有了很大的變化,特別是增長了對 MVC 開發模式的支持,給 Ext JS 開發注入了新的活力,也極大地方便了大型 WEB 項目的開發。本文經過對使用 MVC 先後的比較,透過一個很實用的 MVC 實例,演示了運用 Ext JS4 MVC 開發 Web 前端比用之前的版本要簡單不少;文章最後,本人根據本身在開發過程當中的經歷提出了一些常見的困難以及解決辦法。但願讀者能從中獲得一些啓發和幫助。

相關文章
相關標籤/搜索