天天看點

《Ext JS 4 First Look》翻譯之一:新特性

第一章 新特性                          

extjs 4相對于之前的版本作出了重大的修正。其中包括全新的類系統、新平台的引入、api的修整和加強還有新元件的引入(如新的圖表和圖形元件)。extjs 4提供更快速、更穩定的使用者體驗,并且讓開發人員更容易上手。

在本章我們将學習到下列内容:

  extjs是一個跨浏覽器的富網際網路應用架構,其ui元件容易上手,并全球已有上百萬的開發人員正在使用。

  extjs 2.x對 extjs 1.x進行了重大的重構,其中包括重構元件模型建構方式,并伴随着多個已有元件的重構。而extjs 3.x是向後相容extjs 2.x。

  extjs 3.x 及其之前的版本在應用程式展現上布局(layout)這一操作占據了絕大部分的時間。extjs 4對此進行了重大的改進。而最終生成的html标簽也有所改進。

  對于extjs 4.x sencha團隊進行了多達4000個單元測試,測試覆寫率達到90%,進而為大家提供一個更穩定可用的架構。

  而包含标準api在内的部分api進行了命名習慣的改進,同時使得部分的配置參數更易于使用。

  對于extjs 3.x 如pagingtoolbar,toolbar和spacer均和其他類那樣屬于widgets包。這些類均可通過全局對象ext來直接通路,如ext.pagingtoolbar等。

  但在extjs

4.x所有的類都按功能劃分到相應的包和命名空間。如pagingtoolbar、toolbar、spacer和其他與工具欄相關的類都已打包到

toolbar包中,并且該包下面的類均以ext.toolbar為命名空間。而其他類也作出了類似的改動,具體的類命名變化請參看原書的

appendix a,ext js 4 versus ext js 3 class names。

  若您想繼續沿用extjs 3.x的命名習慣那怎麼辦呢?extjs

4.x為每個類添加了alternateclassname屬性,該屬性表示該類的可選命名,也就是extjs

3.x中的類名,如ext.toolbar.pagingtoolbar的alternateclassname屬性就是

ext.pagingtoolbar。而在建立元件時采用ext.create(classname或者alternateclassname)即可。當

然還是強力推薦使用extjs 4.x的全新類名。

  api文檔位址:http://docs.sencha.com/ext-js/4-0

  打開文檔首先看到的是歡迎頁。home,api

documentation,guides,videos和examples幾個标簽為于文檔的左上角。當我們點選api

documentation标簽後,所有包的清單會在在左方和中間部分呈現。相關的類會以base、view、components、data和

utilities來分類。當我們點選某個類時效果如下:

  

《Ext JS 4 First Look》翻譯之一:新特性

  在頁面的上方,我們可以看到有一個圖示訓示該類是一個單例類還是一個元件。如果是一個元件,那麼就會顯示xtype屬性。(一定要厘清哪個是類,哪個是元件哦,不然以後使用起來就麻煩許多了)

  在類名下面會根據具體類的情況顯示如下菜單項:configs、properties、methods、events、super classes和sub classes。接着還有一個搜尋框,可通過它來查詢該類的特定屬性、配置項、方法等等。

  對于某些類的描述,api文檔中會列出一些執行個體,并且某些執行個體提供了即時預覽的功能,友善我們檢視代碼執行效果。

  在頁面的右邊,會展現類的alternate names、hierarchy和mixns的資訊。

  當大家打開api文檔時會發現有些方法被标記為deprecate,就是說這些方法已被棄用了。

  在api文檔的右上角我們可以看到一個搜尋框,通過它可以在整份文檔中搜尋特定的類、方法、配置項、屬性、事件、組合。

  當然api文檔還提供了官方的guides文檔,讓大家更容易入手ext。

ext js 4 sdk 目錄如下:

《Ext JS 4 First Look》翻譯之一:新特性

 一般我們涉及最多的是ext-all.js和ext.js這兩個檔案。

ext-all.js: 包含整套已壓縮的ext js架構。使用時引入該檔案到html檔案,然後項目中隻需包含該檔案和resource檔案夾即可。目錄結構如下:

     --extjs

          --ext-all.js

          --resources

ext.js: 隻包含ext js已壓縮的基礎庫。使用時引入該檔案到html檔案,然後項目中把src也包含進來。目錄結構如下:

          --ext.js

          --src

我們還看到有*-dev.js和*-debug.js、*-debug-w-comments.js檔案。下面我們逐一了解。

*-dev.js:内含無壓縮代碼和用于debug的代碼,建議在開發和測試環境中使用;

*-debug.js:内含無壓縮代碼但不含用于debug的代碼,建議在測試環境中使用;

*-debug-w-comments.js:内含無壓縮代碼但不含用于debug的代碼,建議在測試環境中使用。

sdk的其他檔案夾:

docs:含完整的api文檔;

examples:含元件的使用執行個體;

overview:含新特性的快速浏覽資料;

pkgs:含extjs的子產品;

resources:含css和圖檔、主題等檔案;

src:含extjs的完整源碼;

welcome:含index.html所需的所有圖檔檔案;

builds:含extjs的額外檔案;

jsbuilder:含jsbuilder工具,用于項目建構。關于jsbuilder的詳細資訊,請浏覽http://www.sencha.com/products/jsbuilder(譯者本人還沒使用過這工具,請指教)

在builders檔案夾中有下面幾個檔案:

ext-all-sandbox.js:沙盒模式的ext-all.js和ext-base.js;

ext-core.js:extjs的核心庫;

ext-foundation.js:extjs的基礎庫。

  最後要講解的是bootstrap.js檔案,它會根據具體的運作環境決定引用ext-all.js還是ext.js檔案。除了下列情況外,bootstrap.js均會引用ext-all.js檔案。

     1.主機名(hostname)為localhost

     2.主機名(hostname)為ip(v4)位址

     3.協定名為file

extjs 4類系統中有如下激動人心的特性:

類定義和對象執行個體化方式;

組合;

自動化getter和setter;

動态加載類檔案;

靜态成員。

  ext js4引入ext.define和ext.create方法來定義類和執行個體化。在本章我們将學習如何定義類和執行個體化對象。

1.3.1.1. 類定義

  extjs 3我們需要繼承object來定義類,代碼如下:

myapp.newclass = ext.extend(object,{

     // 類成員定義

});

  而extjs 4我們可以如下的定義類:

ext.define("myapp.newclass",{

  很明顯,ext.extend已被ext.define代替了。

  讓我們對比一下extjs 3和extjs 4自定義window時的代碼吧!

《Ext JS 4 First Look》翻譯之一:新特性

下面是extjs3的代碼:

ext.namespace("myapp");

myapp.mywindow = ext.extend(ext.window,{

     title: "welcome!",

     initcomponent: function(){

          ext.apply(this, {

               items: [{

                    xtype: "textfield",

                    name: "tfname",

                    fieldlabel: "enter your name"

               }]

          });

          myapp.mywindow.superclass.initcomponent.apply(this, arguments);

     }

var win = new myapp.mywindow();

win.show();

可以看到我們必須使用ext.namespace來注冊自定義的命名空間,否則抛出myapp未定義的異常資訊。與此同時,若

ext.window沒有完成加載,那麼也會抛出ext.window未定義的異常資訊。而extjs 4就省去了這個麻煩。下面是extjs

4的代碼:

ext。define("myapp.mywindow", {

     extend: "ext.window",

          this.items = [{

               xtype: "textfield",

               name: "tfname",

               fieldlabel: "enter your name"

          }];

          this.callparent(arguments);

var win = ext.create("myapp.mywindow");

  extjs 4中以字元串的形式定義類和引用父類,是以并不存在該類未定義的說法。extjs

4的classmanager會檢查ext.window是否已完成加載并已定義,若沒有則推遲myapp.mywindow的執行個體化并自動完成

ext.window的加載。架構會為我們管理各類的加載順序,并且ext.define會自動監測和建立省命名空間,省心多了。

  另外我們可以看到,exjts

3中調用父類方法的語句為myapp.mywindow.superclass.initcomponent.apply(this,

arguments),而extjs 4就簡約為this.callparent(arguments)。

對于override的方法使用this.callparent(arguments)就好比如c#的override方法中使用base.方法名(參數

1.......)。用于把子類的參數傳遞到父類的方法中。除了initcomponent外,常見的使用形式還有:

     ext.define("parent",{

          constructor: function(name){

               this.name = name;

          }

     });

     ext.define("child",{

          extend: "parent",

          constructor: function(name, sex){

               this.sex = sex;

               this.callparent([name]);// 參數為數組

     var c = new child("john huang", "male");

     或者 var c = ext.create("child", "john huang", "male"); // 建議使用該形式執行個體化對象

     console.log(c.name);

     console.log(c.sex);

建議使用ext.create執行個體化對象,這樣可以利用extjs 4類系統的特性。

ext.define是ext.classmanager.create的别名,ext.create是ext.classmanager.instantiate的别名。

組合是extjs4的新特性,可用于實作多繼承。該屬性會以同步方式加載類檔案,并執行個體化該類(譯者推理其内部使用ext.create方法)。其他原文不翻譯了,直接上執行個體吧!

基本用法:

ext.define("myclass.a", {

     showa: function(){

          console.log("a");

ext.define("myclass.b", {

     showb: function(){

          console.log("b");

ext.define("myclass.c", {

     mixins: {

          classa: "myclass.a",

          classb: "myclass.b"

     },

     showc: function(){

          console.log("c");

var objc = ext.create("myclass.c");

objc.showa(); // 控制台結果:a

objc.showb(); // 控制台結果:b

objc.showc(); // 控制台結果:c

方法同名時

情況1——多個mixins類擁有同名函數:

     show: function(){

objc.show(); // 控制台結果:a

           classb: "myclass.b",

           classa: "myclass.a"

那麼

objc.show(); // 控制台結果:b

結論:mixins中後者的方法無法覆寫前者的同名方法。

情況2——mixins類與目前類擁有同名函數:

          classa: "myclass.a"

     show: function(){

objc.show(); // 控制台結果:c

結論:方法的調用遵循最近優先原則,就是先查詢直接類是否有該方法,有則調用,無則查詢mixins中包含的類。

情況3——mixins類與父類擁有同名函數:

     extend: "myclass.b"

結論:方法的調用遵循最近優先原則,優先級順序從高到低——目前類、父類、mixins類。

目前類引用mixins類成員

     alert: function(){

          this.mixins.classa.show();

objc.alert(); // 控制台結果:a

先上代碼吧!

基本使用方式:

     config: {

          name: "john huang",

          sex: "male"

          console.log(this.config.name);

var obja = ext.create("myclass.a");

obja.show(); // 控制台結果:john huang

obja.setname("john");

obja.show(); // 控制台結果:john

console.log(obja.getname()); // 控制台結果:john

console.log(obja.name); // 控制台結果:john

config屬性會将為其屬性自動添加setter和getter函數。

若打算修改setter的行為,可以重寫“apply屬性名”方法,該方法将為setter的内部實作。具體代碼如下:

     applyname: function(val){

          this.name = "dev: " + val;

          console.log(this.name);

var a = ext.create("myclass.a");

a.setname("john");

console.show(); // 控制台結果:dev: john

原文中說除了自動生成getter和setter,還會自動生成resetter。但經過實際操作我并沒發現有此方法,那就需要我們自定義resetter了。在自定義前我們需要了解config屬性、目前類、getter和setter的關系和原理。

下面我們通過執行個體來了解吧

1. console.log(a.config.name); //  控制台結果:john huang

2. console.log(a.name); // 控制台結果:undefined

3. console.log(a.getname()); // 控制台結果:dev: john

4. console.log(a.name); // 控制台結果:dev: john

5. console.log(a.config.name); //  控制台結果:john huang

語句3的結果是不是和我們預想的john

huang有所出入呢?不是說調用setname的時候才會調用applyname嗎,為啥調用getname的時候也會調用applyname呢?其實

調用getname的時候不定會調用applyname方法,隻有當語句2結果為undefined時才會有如此的效果,而且隻有首次調用時會調用

applyname方法。如果在語句3前執行了a.name =

"john"或者a.setname("john"),那麼就不調用applyname方法。

  分析:

          其實getname内部實作在首次調用和第n次調用時是不同的。

          首次調用getname方法時的内部實作步驟如下:

          1. 檢查對象是否有name屬性,有則執行步驟2,無則執行步驟3;

          2. 傳回name屬性,并更新内部實作;

          3. 以config.name為參數執行applyname函數,因為applyname函數體為this.name =

.....,就會添加name屬性到對象中,然後更新内部實作。(若applyname函數體無this.name=...的語句,那麼getname的

傳回值将是undefined)

          第n次調用getname方法是的内部實作如下:

          function(){ return this[q]; },直接傳回對象的屬性。

  結論: setter和getter是将config的成員屬性複制(注意:為淺複制)為目前類的成員屬性,然後對成員屬性進行後續操作。

  是以我們在重寫applyname時需要遵守下列原則。

     不要修改config的成員屬性值

  而在類内部成員函數中通路config的成員屬性時建議如下操作:

     },

     showname: function(){

          var name = this.name || this.config.name;

          console.log(name);

     updatename: function(val){

          this.name = val;

現在大家應該對getter和setter、config、目前類的關系有所了解了。下面我們來自定義resetter吧!

          name: "john huang"

     /*

     ** @param configproperties{object/string/array} config的成員屬性名

     ** @return {void}

     */

     reset: function(configproperties){

          if ("string" === typeof configproperties){

               if (this.config.hasownproperty(configproperties)){

                    this[configproperties] = this.config[configproperties];

               }

          else if ("object" === typeof configproperties && null !== configproperties){

               var properties = configproperties;

               if ("[object object]" === object.prototype.tostring.call(configproperties)){

                    properties  = object.getownpropertynames(configproperties);

               for (var i = properties.length - 1; i >= 0; --i){

                    var property = properties[i];

                    if (this.config.hasownproperty(property)){

                         this[property] = this.config[property];

                    }

          }

對象執行個體化時設定config成員值

     在constructor方法中使用this.initconfig(參數對象),代碼如下:

     ext.define("a", {

          config: {

               name: "john huang",

               sex: "male"

          },

          constructor: function(config){

               this.initconfig(config);

     var a = ext.create("a", {

          name: "extjs",

          sex: "i don't know"

     }); 或 new a({

     });

  作為extjs 4的新特性,extjs架構為動态類加載提供一個整合依賴關系的管理系統。

  但我們應該避免在産品中使用該特性,因為類檔案的加載操作将嚴重影響使用者體驗。而在開發環境中使用該特性将有利于調試和項目檔案結構的組織。

  因發現原文中并未詳細地描述動态類加載的相關内容,以下内容為結合原文及api文檔總結而成,若有纰漏請大家多多指點。

  動态加載分類:

同步加載:加載類檔案時javascript線程将被阻塞直至類檔案加載完成或目前類定義的操作被阻塞直至類檔案加載完成。

涉及的類、方法和屬性:

ext.create(加載類檔案并執行個體化對象)

ext.loader.syncrequire(别名:ext.syncrequire) (加載類檔案)

ext.class.requires(加載類檔案)

ext.class.mixins(加載類檔案并執行個體化對象)              

ext.class.extend(加載類檔案并執行個體化對象)

ext.data.field.type(加載類檔案)

ext.data.association.associatedmodel(或ext.data.association.model) (加載類檔案)

ext.data.store.model(加載類檔案)

說明:上

述1、2的是調用該語句時就會發起xhq請求加載類檔案,且javascript線程将被阻塞;3、4、5、6、7、8的是在類檔案加載完成後解析代碼時

發起xhq請求,隻阻塞該類的定義操作,而不會阻塞javascript線程執行類定義以後的代碼,若在執行個體化期間使用而事先未加載該類檔案則會報異常

(ext.data.store.model就是可以在執行個體化store對象時設定其model值,若此時model指向的類檔案沒有加載那麼就會抛出異

常)

。例子:

 ext.define("test", {extend: "b"}); var test = new test(); 會報test未定義的異常。

異步加載:加載類檔案時javascript線程可繼續執行其他代碼(包括類定義的操作),當類檔案加載完成後可觸發回調事件。

ext.loader.require(别名:ext.require)(加載類檔案)

ext.class.uses(加載類檔案)

說明:上述1的是調用該語句時就會發起xhq請求加載類檔案;2的是在類檔案加載完成後解析代碼時發起xhq請求。

  啟動動态加載功能:

     ext.loader.setconfig({ enabled:true }); api文檔表示預設為false,但實踐證明預設為true。

  死鎖問題:

     動态加載類檔案時,若多個類檔案間彼此使用同步方式動态加載對方并形成類檔案加載的閉環,便會産生死鎖問題,并使浏覽器無法正常工作。執行個體如下:

          extend: "b"

     ext.define("b", {

          mixins: {

               c: "c"

     ext.define("c", {

          requires: ["a"]

     ext.syncrequire("a"); 或 ext.create("a"); // 發起類檔案加載請求

     說明:上述定義a、b、c三個類,并且分别使用extend、mixins、requires三種ext.class的屬性來設定互相依賴關系進而形成類檔案加載的閉環。

     消除死鎖:

          1. 對于該類執行個體化時非前提條件的類檔案,使用ext.class.uses屬性異步加載;

          2. 使用ext.require方法發起異步類檔案加載請求。(不建議使用該方法,因為應在類的設計階段就決定哪些依賴類需要用requires哪些可以用uses來加載)

     可通過ext.class.statics屬性來設定類的靜态成員,具體使用如下:

          statics: {

               count: 0,

               appname: "a"

          constructor: function(){

               ++this.self.count; 或 ++this.statics().count;

          getcount: function(){

               return this.statics().count;

          getappname: function(){

               return this.self.appname;

     var a = ext.create("a");

     a.getcount(); // 結果:1

     a.getappname(); // 結果:"a"

     a.count; // 結果:1

     a.appname; // 結果:"a"

     說明:

          類定義内部不能使用"this.statics.成員名"的方式通路靜态成員,而要使用"this.self.靜态成員名"或"this.statics().靜态成員名";

          類定義外部使用"類名.靜态成員名"來通路類靜态成員。

          譯者了解:this.self擷取的就是類本定義本身,是以"this.self.靜态成員名"實際與"類名.靜态成員名"是一樣的。而"this.statics().靜态成員名"是另一種方法通路類靜态成員。

  extjs4在架構、核心庫群組件上都進行了重構。通過上面的學習我想大家對新的類系統已經有了較全面的了解了。現在我們一同來學習extjs4的元件吧。當然有些變化并不相容extjs3,是以下面的内容會介紹如何從extjs3遷移到extjs4。

     在extjs4之前的版本中,若我們打算使用除extjs外如jquery等工具庫時,我們需要引入由ext提供的擴充卡檔案,如ext-jquery.adapter.js。

引入的檔案如下:

     ext-all.js

     ext-jquery.adapter.js

     jquery.js

ext擴充卡、第三方工具庫關系圖:

《Ext JS 4 First Look》翻譯之一:新特性

而在extjs4中就省心多了,無需擴充卡的支援直接就可以引用如jquery的工具庫。

      ext-all.js

      jquery.js

關系圖如下:

《Ext JS 4 First Look》翻譯之一:新特性

     作用:在同一個頁面中,同時使用extjs4和其他版本的extjs。

     相關檔案:

                    ext-all-sandbox.js

                    ext-all-sandbox-debug.js

                    ext-all-sandbox-dev.js

                    ext-sandbox.css

     使用注意點:使用ext4代替ext關鍵字

                         引入的檔案如下:

                              extjs3的檔案

                              ext-sandbox.css

                              ext-all-sandbox.js

     extjs一直提供基于自身的面向元件的類系統和架構,和相應配套的layout、state、utilities和data包。extjs 4的架構與extjs 3相似。

《Ext JS 4 First Look》翻譯之一:新特性

     sencha誕生于2010年,與此同時還推出了extjs的兄弟産品sencha touch。sencha

touch是移動裝置的javascript架構,其架構不同于extjs3。到了exjts4

release版時,sencha決定融合extjs4和touch的架構,而sencha平台就是以而誕生了。平台中提供部分通用代碼如data、

layouts、大部分utility的方法和新的charting和animation包。通過這樣,sencha的團隊就能持續提供穩定可用容易維護

的産品代碼了。當然這樣就降低作為使用者的我們學習touch的曲線了。(要知道extjs的因功能強大,學習曲線可陡着呢)

《Ext JS 4 First Look》翻譯之一:新特性

  該包包括負責資料加載和儲存的類。現在讓我們了解一下它吧。

store(資料倉庫):過去我們需要事先确認加載的資料是json還是xml格式進而選擇使用jsonstore還是xmlstore,現在

extjs4的最新store類會自動監測加載的資料格式,并且store還提供了排序、過濾的功能,通過最新的reader類還能從服務端讀取格式化數

據。

model(資料模型):model是extjs獨有的類,類似于extjs舊有版本的record,但又提供了如關聯關系(associations)和資料驗證(validations)的功能。

proxy(資料代理):proxy類負責加載和儲存資料,和接收reader和writer類的執行個體對象。我們可以将某類型的proxy附加到

store或model類中(不像extjs舊有版本那樣proxy必須附加到store中)。extjs4還新增了localstorageproxy

和sessionstorageproxy,提供将資料儲存到html5 local storage和session storage的功能。

  提醒:上述的内容不是100%相容extjs舊有版本的代碼。

     extjs4引入全新的draw包,提供基于html5标準的自定義畫圖功能。我們可以畫如方形、圓形或者是文本等基本形狀。同時它也提供了通過svg path畫複雜形狀的功能。draw包是chart包的基礎。

 extjs3引入全新的chart元件,但其依賴于flash。而extjs4的chart元件不再依賴flash了,而是純javascript驅動

基于svg(scalable vector graphics)、canvas和vml(vector markup language)。

     通過extjs4我們可以畫任何想要的圖表。基本的圖表有bar/column、line/area、scatter、radar,我們可以通過這些基本的圖表來組合出各種自定義的圖表。

 布局是extjs中最重要和強大的特性之一。ext2中的布局渲染速度快但不夠靈活,ext3中靈活性上進行了修改但犧牲了新能。而ext4中重寫了布

局引擎,現在不僅更靈活了而且渲染速度也更快了。此外還新增了如docklayout、toolbarlayout和fieldlayout等布局。

     表格是我們用得最多的一個extjs元件。extjs4對其進行了重寫,使其更快、更容易自定義并擁有更高的性能。

     在extjs3使用表格一次展現上千條資料時會出現各種性能問題,解決方法是使用一個插件來是表格支援緩存。而extjs4的表格就内置緩存機制,現在不用再擔心該問題了。

 表格的編輯功能在extjs4中也得到了改進。在extj3中如果我們想使用表格來編輯資訊,那麼就要用一個特殊的表格——editorgrid,或者

是使用第三方插件roweditor。在extjs4中,内置了roweditor插件,我們隻需将該插件附加到表格(grid)中即可編輯表格資料信

息。(譯者語:将第三方官方化是使産品更穩定的做法)

 在extjs3中若我們想為表格添加新功能,我們必須繼承表格後自定義表格類或通過自定義插件的形式來實作。extjs4引入了

ext.grid.feature,其提供了基本的特性讓我們自定義新的表格特性,而不必修改表格的核心部分,進而是表格更穩定。(譯者語:本人還沒搞懂

該方式和plugin的差別,隻是大概明白這樣做的理由是把可擴充的部分獨立出來,免除核心部分被修改而表格功能又能最大化的靈活修改)

 extjs舊有版本中會完整地建立表格的html标簽,不管實際上是否使用了其某功能(編輯、行擴充),這使得産生了不少無用html标簽也降低了性能

(譯者語:曾試過在ie6個上一次加載8000個html标簽,足足加載了2分鐘啊)。extjs4遵循節約原則,開啟的功能才會為其建立html标簽,

是以我們再也找不到extjs3中的listview元件了(listview是grid的輕量級元件,隻用于資料展現)。

     表單是另一個我們經常使用到的元件,而extjs4為其增添了很多新特性。首先是不再限制表單隻能用formlayout作為其布局,而是可以使用任何extjs提供的布局方式。

     另外extjs4還提供了fieldcontainer類,用于把其他元件(如表格等)打包到到表單中。

     關于資料合法性驗證方面也作出了重大的修整。

     通過javascript生産通路性良好的應用是一件十分困難的事情。而extjs4引入了下列三種特性來幫助我們提供應用的可通路性。

     1. 每個元件均有特性來支援aria(accessible rich internet application);

     2. 支援鍵盤導航;

     3. 提供全新的主題來提供通路性。

     大家都知道修改extjs舊有版本的主題是一件多麼痛苦的事情,而extjs4通過使用sass和compass這兩種優秀的工具使得自定義主題變得一件寫意的事情。

     在這一章我們對extjs4新的類系統有了相當程度的了解,對extjs4相對于舊有版本的修整也有了大概的了解。我想大家應該和我一樣對這些新特性感到興奮,當然腦海中對extjs4也産生了很多的問号。那下一章我們将一起繼續extjs4的旅途^_^!

 轉載請标明出處哦!http://www.cnblogs.com/fsjohnhuang/archive/2013/01/29/2880705.html

繼續閱讀