10 大Extjs開發應該避免的錯誤

1.過多不必要的組件嵌套結構

最常見的錯誤之一是開發人員沒有理由的嵌套組件。這樣做不僅會影響程序性能,也能導致雙邊框或意想不到的佈局行爲。在下面的例子1A,我們有一個panel,其中包含一個grid。在這種情況下,該面板是不必要的。在例1B所示,額外panel可以被消除。forms, trees, tab panels和grids擴展自panel,因此在使用這些組件時,應該特別注意不必要的嵌套使用組件。

items: [{
    xtype : 'panel',
    title: ‘My Cool Grid’,
    layout: ‘fit’,
    items : [{
        xtype : 'grid',
        store : 'MyStore',
        columns : [{...}]
    }]
}]

Example 1A. BAD:這個panel不是必須的。

layout: ‘fit’,
items: [{
    xtype : 'grid',
    title: ‘My Cool Grid’,
    store : 'MyStore',
    columns : [{...}]
}]

Example 1B. GOOD:grid已經是一個panel了,因此可以直接用panel的屬性在grid上。

2.內存泄漏導致清理未使用組件失敗

許多開發人員驚訝不知道爲什麼他們的應用越來越慢,用的時間越長越慢。在用戶使用應用過程中未能清理未使用的組件是應用程序失敗的最大的原因。在下面的例子2A,每次用戶右鍵點擊grid行,創建一個新的右鍵菜單。如果用戶在此應用程序右擊grid行數百次,將會有數百個右鍵菜單沒有銷燬。對開發者和用戶來說,應用程序看起來是正常的那是因爲每次看到的是最後一次創建的右鍵菜單,其它的都被隱藏了。應用程序用的內存將會不斷增加,最終導致較慢的操作或瀏覽器崩潰。例2B正確,因爲右鍵菜單創建一次在grid初始化的時候,可以每次重用已經創建的右鍵菜單,然而當grid銷燬時,右鍵菜單仍然存在雖然它不再需要了。最好情形是例子2C,右鍵菜單會被銷燬當grid銷燬時。

Ext.define('MyApp.view.MyGrid',{
    extend : 'Ext.grid.Panel',
    columns : [{...}],
    store: ‘MyStore’,
    initComponent : function(){
        this.callParent(arguments);
        this.on({
            scope : this,
            itemcontextmenu : this.onItemContextMenu
        });
    },
 
    onItemContextMenu : function(view,rec,item,index,event){
        event.stopEvent();
        Ext.create('Ext.menu.Menu',{
            items : [{
                text : 'Do Something'
            }]
        }).showAt(event.getXY());
 
    }
});

Example 2A. BAD:每次右擊都會創建右鍵菜單但一直沒有銷燬。

Ext.define('MyApp.view.MyGrid',{
    extend : 'Ext.grid.Panel',
    store : 'MyStore',
    columns : [{...}],
    initComponent : function(){
        this.menu = this.buildMenu();
        this.callParent(arguments);
        this.on({
            scope : this,
            itemcontextmenu : this.onItemContextMenu
        });
    },
 
    buildMenu : function(){
        return Ext.create('Ext.menu.Menu',{
            items : [{
                text : 'Do Something'
            }]
        });
    },
 
    onItemContextMenu : function(view,rec,item,index,event){
        event.stopEvent();
        this.menu.showAt(event.getXY());
    }
});
 


Example2B. BETTER: 菜單在grid創建時創建,每次右擊可以重用已有菜單。

Ext.define('MyApp.view.MyGrid',{
    extend : 'Ext.grid.Panel',
    store : 'MyStore',
    columns : [{...}],
    initComponent : function(){
        this.menu = this.buildMenu();
        this.callParent(arguments);
        this.on({
            scope : this,
            itemcontextmenu : this.onItemContextMenu
        });
    },
 
    buildMenu : function(){
        return Ext.create('Ext.menu.Menu',{
            items : [{
                text : 'Do Something'
            }]
        });
    },
 
    onDestroy : function(){
        this.menu.destroy();
        this.callParent(arguments);
    },
 
    onItemContextMenu : function(view,rec,item,index,event){
        event.stopEvent();
        this.menu.showAt(event.getXY());
    }
});


Example 2C. BEST: 當grid銷燬時,右鍵菜單也銷燬了。

3.巨大的控制器

你經常驚訝的發現一個控制器有數千行代碼。我們傾向於按功能細分我們的控制器。例如,一個訂單處理應用有可能有項目,出貨量,顧客查找等。在瀏覽維護代碼時將會更容易。一些開發者喜歡劃分控制器按view。例如如果應用有一個grid和form,有一個控制器管理grid,一個控制器管理form。沒有一個“正確”一直方式劃分控制邏輯。僅僅記住,控制器可以跟其它的控制器交互。在例子3A,你能知道怎麼獲得另一個控制器的引用調用控制器的方法。

 

this.getController('SomeOtherController').runSomeFunction(myParm);

Example3A.獲得另外一個控制器的引用並調用方法。

或者你可以觸發一個應用級的事件任何一個控制器都可以監聽到。例子3B和3C,你能明白一個控制器可以觸發一個應用級事件另一個控制器可以監聽到。

MyApp.getApplication().fireEvent('myevent');


Example3B 觸發一個應用級事件。

MyApp.getApplication().on({
    myevent : doSomething
});

Example 3C.另外一個控制器可以監聽到應用級事件。

注:用Ext JS4.2開始它變得更容易使用多個控制器 - 他們可以觸發事件,其他控制器可以直接

4.源代碼文件夾結構

這不會影響性能或操作,但你應用程序的結構,很難擴展。隨着你的應用程序的發展,查看源代碼和增加新的特性或功能將會非常容易組織你的源代碼。許多開發者把多個views放到一個目錄如例子4A。我們建議組織views按功能劃分如例子4B。

Example4A. BAD:所有views在同一目錄。

Example4B. GOOD: views組織按功能劃分。

5.使用全局變量

 衆所周知使用時不好的,但在很多應用我們仍然能夠看見使用全局變量。在應用程序中使用全局變量最明顯的問題就是有可能全局變量名稱衝突,調試起來有很困難確定出問題代碼位置。取代全局變量,我們把一些屬性放到一個單例類,對屬性操作時用getter和setter方法。例如,需要記住最後選中的客戶。你有可能定義一個變量如例5A.非常容易在你程序有一樣的變量名稱的變量被定義。

myLastCustomer = 123456

Example5A. BAD:定義全局變量存儲最後客戶。
更好的方法是創建一個類,用類的屬性存儲原來全局變量存儲的值。我們將創建名爲Runtime.js文件存儲運行時一些全局的屬性。例子5B顯示了Runtime.js整個代碼結構的位置。

Example5B. Runtime.js文件位置

例5C顯示了Runtime.js文件的內容,例5D顯示怎麼在app.js中動態加載定義的類。例5E和5F顯示了在應用其它地方用get和set操作這些屬性。

Ext.define(‘MyApp.config.Runtime’,{
    singleton : true,
    config : {
        myLastCustomer : 0   // initialize to 0
    },
    constructor : function(config){
        this.initConfig(config);
    }
});

Example5C. Runtime.js文件樣例代碼顯示了保存一個應用程序全局屬性

Ext.application({
    name : ‘MyApp’,
    requires : [‘MyApp.config.Runtime’],
   ...
});

Example5D.在app.js文件添加必須類MyApp.config.Runtime。

MyApp.config.setMyLastCustomer(12345);

Example5E. 怎麼設置lastcustomer。

MyApp.config.getMyLastCustomer();

Example5F.怎麼獲得lastcustomer。

6.id的濫用

我們不建議使用組件的ID屬性,因爲每個ID都必須是唯一的。這太容易使用相同的ID不止一次,這將導致重複的DOM ID(名稱衝突)。相反,讓框架自動生成ID用Ext JS的ComponentQuery,就不會給Ext JS組件指定一個重複的id。例如6A顯示一個應用程序有兩個不同的保存按鈕,這兩者id一樣都是‘SaveButton',雖然是兩個代碼段但兩個按鈕id衝突。很明顯在一個大的應用程序很難一下確定組件id名稱衝突。

// here we define the first save button
xtype : 'toolbar',
items : [{
    text : ‘Save Picture’,
    id : 'savebutton'
}]
 
// somewhere else in the code we have another component with an id of ‘savebutton’
xtype : 'toolbar',
items : [{
    text : ‘Save Order’,
    id : 'savebutton'
}]


Example6A. BAD: 錯誤的組件id賦值導致id相同衝突。

相反,如果你想手動標識每個組件,你可以用'itemid'替代'id'如例如6B所示。解決了名稱衝突,我們用itemid仍然可以得到一個組件的引用。有許多方法通過itemId獲得一個組件引用。如例6C所示的幾種方法。

xtype : 'toolbar',
itemId : ‘picturetoolbar’,
items : [{
    text : 'Save Picture',
    itemId : 'savebutton'
}]
 
// somewhere else in the code we have another component with an itemId of ‘savebutton’
xtype : 'toolbar',
itemId: ‘ordertoolbar’,
items : [{
    text : ‘Save Order’,
    itemId: ‘savebutton’
}]

Example6B. GOOD: 創建帶屬性itemId的組件

var pictureSaveButton = Ext.ComponentQuery.query('#picturetoolbar > #savebutton')[0];
 
var orderSaveButton = Ext.ComponentQuery.query('#ordertoolbar > #savebutton')[0]; 
 
// assuming we have a reference to the “picturetoolbar” as picToolbar
picToolbar.down(‘#savebutton’);

Example6C. GOOD:引用組件通過itemId。

7.不可靠的引用組件

我們有時候看到用組件的位置去獲得組件的引用。這是應該避免的,因爲組件數組很容易添加,刪除,導致組件位置發生變化。例7A一組常見情況。

var mySaveButton = myToolbar.items.getAt(2);
 
var myWindow = myToolbar.ownerCt;

Example7A. BAD: 避免通過組件位置獲得組件的引用。
相反,使用ComponentQuery,或組件的“up”或“down”方法去獲得組件引用,如例7B中所示。隨後代碼即使發生了變化,組件的結構和順序變化後獲取組件代碼出錯的可能性小。

var mySaveButton = myToolbar.down(‘#savebutton’);    // searching against itemId
 
var myWindow = myToolbar.up(‘window’);


Example7B. GOOD: 用ComponentQuery獲得組件的引用。

8.不遵守大寫/小寫命名約定

在Sencha命名components, properties, xtypes等時需要遵守一些大小寫規則。爲了避免混淆,並讓你的代碼乾淨,你應該遵循同樣的標準。如例8A顯示了一些不正確情況。例8B顯示了相同的情況下,用正確的大寫/小寫命名約定。

Ext.define(‘MyApp.view.customerlist’,{          // should be capitalized and then camelCase
    extend : ‘Ext.grid.Panel’,
    alias : ‘widget.Customerlist’,                       // should be lowercase             
    MyCustomConfig : ‘xyz’,                            // should be camelCase
    initComponent : function(){
        Ext.apply(this,{
            store : ‘Customers’,
            ….
        });
        this.callParent(arguments);
    }
});

Example8A. BAD: 粗體代碼用了錯誤的大小寫命名。

Ext.define(‘MyApp.view.CustomerList’,{      
    extend : ‘Ext.grid.Panel’,
    alias : ‘widget.customerlist’,      
    myCustomConfig : ‘xyz’,            
    initComponent : function(){
        Ext.apply(this,{
            store : ‘Customers’,
            ….
        });
        this.callParent(arguments);
    }
});

Example8B. GOOD: 上面代碼用了正確的大小寫。

此外,如果你觸發自定義事件,這個事件名稱最好全部小寫。當然,如果你不遵守這些約定你代碼仍然可以正常運行,但爲什麼要寫一些不標準,不乾淨的代碼呢?

9.創建組件依賴父組件佈局

如例9A,這個panel將會一直有屬性‘region:center’,如果你想重用這個組件在“west”區域將會不能正常工作。

Ext.define('MyApp.view.MyGrid',{
    extend : 'Ext.grid.Panel',
    initComponent : function(){
        Ext.apply(this,{
            store : ‘MyStore’,
            region : 'center',
            ......
        });
        this.callParent(arguments);
    }
});
 

Example9A. BAD: ‘center’區域不應該這賦值。

相反,組件的佈局信息應該在組件創建時賦值如例9B。通過這種方式,在你喜歡的任何地方你可以重用此組件,並不會受到佈局配置的制約。

Ext.define('MyApp.view.MyGrid',{
    extend : 'Ext.grid.Panel',
    initComponent : function(){
        Ext.apply(this,{
            store : ‘MyStore’,
            ......
        });
    }
});
 
// specify the region when the component is created...
Ext.create('MyApp.view.MyGrid',{
    region : 'center' 
});
 

Example9B. GOOD: 設置區域在組件創建時。

如例9C所示,你也可以提供一個默認的“region”,,有必要可以重寫的組件的“region”屬性。

Ext.define('MyApp.view.MyGrid',{
    extend : 'Ext.grid.Panel',
    region : 'center', // default region
    initComponent : function(){
        Ext.apply(this,{
            store : ‘MyStore’,
            ......
        });
    }
});
 
Ext.create(‘MyApp.view.MyGrid’,{
    region : ‘north’, // overridden region
    height : 400
});

Example9C. Also GOOD:設置默認的“region”屬性,有必要可以覆蓋此屬性。

10.代碼比實際的需要更復雜

有很多時候,我們看到了比需要更復雜的代碼。這通常是不完全熟悉每個組件的方法的結果。我們看到的最常見的情況之一是代碼單獨加載每個表單字段從數據記錄。如例10A所示。

//  suppose the following fields exist within a form
items : [{
    fieldLabel : ‘User’,
    itemId : ‘username’
},{
    fieldLabel : ‘Email’,
    itemId : ‘email’
},{
    fieldLabel : ‘Home Address’,
    itemId : ‘address’
}];
 
// you could load the values from a record into each form field individually
myForm.down(‘#username’).setValue(record.get(‘UserName’));
myForm.down(‘#email’).setValue(record.get(‘Email’));
myForm.down(‘#address’).setValue(record.get(‘Address’));
 

Example10A. BAD: 錯誤的手動加載表單屬性從record。

用loadRecord方法加載所有的字段從record到表單的每個字段用一行,而不是手動加載每個值。關鍵是確保表單字段的“name”屬性跟record的字段的name屬性一致如10B所示。

items : [{
    fieldLabel : ‘User’,
    name : ‘UserName’
},{
    fieldLabel : ‘Email’,
    name : ‘Email’
},{
    fieldLabel : ‘Home Address’,
    name : ‘Address’
}];
 
myForm.loadRecord(record);
 


Example10B. GOOD: 用一行代碼加載所有字段值。

這僅僅是個比需要更復雜的例子代碼.關鍵是清楚每個組件的方法和例子以確保用了簡單合適的技術。

文章其餘部分是CNX公司信息如下

CNX Corporation is a Sencha Certified Select Partner. The Sencha Partner Network is a valuable extension of the Sencha Professional Services team.
CNX has been a leader in the development of custom business apps since 1996. CNX standardized its browser-based user interface development on Ext JS in 2008, adding Sencha Touch as a standard for mobile development in 2010. We have created world-class web apps for customers around the world in many industries including Education, Finance, Food, Law, Logistics, Manufacturing, Publishing and Retail. Our development teams are based at our corporate office in downtown Chicago and can handle projects of any scale. CNX can work independently or with your team to achieve project goals in a fast and cost-effective manner. Check us out at http://www.cnxcorp.com.
Written by Sean Lanktree
Sean is an Ext JS Professional Services Lead at CNX Corporation.

未作翻譯。

原英文文章地址 http://www.sencha.com/blog/top-10-ext-js-development-practices-to-avoid/

轉載請註明出處,謝謝。








 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章