運用Ext JS 4的MVC架構

Ext JS 4簡介javascript

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

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

  • 渲染效率提升
  • 全部類都通過調優,包括最影響渲染效率的佈局引擎重寫。
  • 命名空間 命名空間是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.  });   
  8.      

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

  • 全新的類系統 因爲篇幅的限制,具體請參閱官方文檔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 JS 4的利器 : Sencha Architect 2」中,該章節會詳細介紹怎樣用該工具開發MVC模式的Ext JS程序。程序員

爲何要運用MVC架構MVC的概念web

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.文件結構

文件結構

  • JavaScript腳本都按職責存放在不一樣的JS文件中。
  • 不一樣的JS文件按MVC目錄命名約定"controller"、"store"、"view"、"model"分類存放。
  • 整個工程裏只有一個HTML文件,並且只做爲入口文件

清單 2.入口文件

  1. <html
  2.  <head
  3.     <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
  4.     <title>Demo</title
  5.     <link rel="stylesheet" type="text/css"   
  6.      href="http://extjs.cachefly.net/ext-4.0.2a/resources/css/ext-all.css"/> 
  7.     <script type="text/javascript"   
  8.      src="http://extjs.cachefly.net/ext-4.0.2a/ext-all-debug.js"></script
  9.     <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的構建,後面會具體介紹。

  • 總體邏輯更加條理

入口定義在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.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 JS 4的利器: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.視圖設計

視圖設計

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

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

圖 3.模式切換

視圖設計

  • 屬性設置面板

圖 4.屬性面板

屬性面板

  • 項目導航面板

圖 5.導航面板

導航面板

示例和代碼

本文所指的入口代碼是什麼?

相信讀者對Ext.onReady()不會陌生,本文所指的入口代碼的功能與Ext.onReady() 是同樣的,不一樣在於,新版的Ext JS 4將命名空間和全局application變量天然地融合在入口函數中,開發者很容易從Model、View或Controller中調用application中的函數、變量和事件,並從使用命名空間的過程當中獲益,使得MVC的開發變得更方便。

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

  • 入口代碼

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

清單 3. 入口代碼

  1. /* 動態加載依賴的前提 */   
  2.  Ext.Loader.setConfig({   
  3.     enabled: true,     
  4.     paths: {   
  5.         'Extention': 'js'// 設置一個擴展命名空間,區分工具生成的代碼  
  6.     }   
  7.  });   
  8.  
  9.  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.圖表子頁面控制器代碼

  1. Ext.define('MyApp.controller.ChartCtrl', {   
  2.     extend: 'Ext.app.Controller',   
  3.  
  4.     stores: [   
  5.         'MyChartStore',   
  6.         'LatestMonths'  
  7.     ],   
  8.  
  9.     refs: [   
  10.         {   
  11.             ref: 'tabPanel',   
  12.             selector: '#tabPanel'  
  13.         },   
  14.         {   
  15.             ref: 'latestMon',   
  16.             selector: '#comboLatest'  
  17.         }   
  18.     ],   
  19.     /* 請參看 init 中的事件綁定 */   
  20.     onComboboxSelect: function(combo, records, options) {   
  21.         console.log('onComboboxSelect');   
  22.         this.getMyChartStoreStore().load();   
  23.         return false;   
  24.     },   
  25.  
  26.     init: function(application) {   
  27.         // 綁定月份下拉框的 select 事件  
  28.         this.control({   
  29.             "#comboLatest": {   
  30.                 select: this.onComboboxSelect   
  31.             }   
  32.         });   
  33.           
  34.  /* 綁定 store 的 load 事件,完成動態數據效果(演示)  
  35.            實際中項目不須要這樣,  
  36.    應在 store 上定義 proxy 從服務器拿數據  
  37.  */   
  38.         this.getMyChartStoreStore().on(   
  39.         {   
  40.             'load' : function(me, operation, eOpts) {   
  41.                 me.loadData(generateData(8));   
  42.             }   
  43.         }   
  44.         );   
  45.  // 初始化月份下拉框的值爲」1「  
  46.         this.getLatestMon().setValue('1');   
  47.  // 爲圖表加載數據,此處顯示調用,  
  48.  // 是由於此 store 屬性 autoLoad 定義 false   
  49.         this.getMyChartStoreStore().load();   
  50.     }   
  51.  });   
  52.   
  • 運行結果

圖 6.表單

表單

圖 7.表格

表格

 

圖 8.圖表

圖表

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

  • 關於Sencha Architect代碼的管理

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

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

  • 關於Controller

(1)劃分好Controller的範圍和生命週期。

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

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

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

  • 關於store多實例

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

  • 關於TreePanel對屬性設置的要求

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

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

(2)設置好樹節點所用到的Model中的idProperty屬性。這個屬性決定點擊樹的節點往下鑽取時往服務器傳遞的參數。

總結

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

相關文章
相關標籤/搜索