ExtJs最佳实践

时间:2011-01-22 20:24:34

标签: javascript extjs

似乎大多数ExtJS书籍和ExtJS介绍只关注显示所有不错的功能,但大多数并没有真正解释如何使用ExtJS构建稳定,可维护的布局/应用程序。如何写它们而不会造成难以理解的混乱......

我没有特定的应用程序,我只是想知道如何“美化”我的ExtJS代码。因此,如果您需要一个示例,项目管理(库)或类似工作板的“常规”应用程序最能描述我的想法。

当我想用ExtJS为客户端代码构建这些应用程序时,任何人都可以分享一些关于如何构建这些应用程序的良好链接或建议吗?

提前致谢, 干杯

3 个答案:

答案 0 :(得分:7)

在我公司的主要代码审核人员执行中:

  • 作为组件总是与PAPA交谈 - 永远不要给你兄弟姐妹(允许自己的孩子离开)
  • 尽量避免冒泡
  • 如果你遵循#1,你不需要使用Ext.getCmp()它太贵了,所以不要
  • 考虑团队中其他人可重复使用的每个组件
  • 在命名空间中使用适当的层次结构(并使用命名空间)

在以下文档中只有几个主要规则......:)

答案 1 :(得分:2)

有用的链接:

答案 2 :(得分:2)

Sencha博客关于十大最差做法的文章值得一读。

Sencha Top 10 Worst Practices

博客文章摘要

**请注意,所有赠送金额均归原始博文的合法所有者所有。

<强> 1。组件结构的过度或不必要的嵌套

有时候开发人员会使用冗余的嵌套组件,这可能会导致应用程序出现意想不到的缺乏吸引力的美学现象,例如双边框或意外的布局行为。

BAD

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

GOOD

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

在上面的示例中,嵌套面板是多余的,因为grid是面板的扩展。    此外,形式,树木,标签面板等其他元素也是面板的延伸。

<强> 2。由于未能清除未使用的组件而导致内存泄漏。

这是有史以来最重要的规则之一。在任何编程语言中它都非常非常     重要的是要确保不再使用的组件被正确丢弃,即使在     像Java这样的语言,GC正在为我们做所有的清理,我们应该确保我们没有持有     在我们完成它们之后的任何对象。

BAD

    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());

        }
    });

每次用户右键单击网格行时,都会创建一个新的上下文菜单。哪个看起来不错,因为我们     只能看到最新的菜单。

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());
        }
    });

这比初始的更好。每次用户时它都使用相同的菜单对象     右键单击网格视图。但是,即使我们杀死网格视图,它也会使菜单保持活动状态     不是我们需要的。

GOOD

    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());
        }
    });

在上面的视图中,当网格被破坏时,我们也会破坏菜单。

第3。怪物控制器

有些人像怪物一样编码......开玩笑,但有一些大控制器(不只是控制器,其他组件也是:))    它由数千行代码组成,完成所有与彼此无关的东西。

找到一种方法可以在一开始就将应用程序分解为不同的处理单元    项目,这样你就不会得到一个处理应用程序中所有进程的巨型控制器。

建议:

以不同的方式分解你的申请

APP功能(在订单处理应用程序中 - >订购,交付,客户查询等)

VIEWS(网格,表格,等等)

在ExtJS中,控制器可以相互通信。

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

还可以触发任何控制器可以侦听的应用程序级事件。

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

另一个控制器也会侦听应用级事件。

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

<强> 4。源代码的文件夹结构不佳

在任何应用程序中,良好的结构都非常重要,因为它提高了项目的可读性和可维护性。    不是将所有控制器放在一个文件夹中,而是将所有视图放在另一个文件夹中,最好以逻辑方式构建它们         根据他们的功能。

<强> 5。使用全局变量

为什么使用全局变量不好?             有时它不清楚它所拥有的实际价值,因此它可能导致许多混淆,如

  • 名称冲突
  • 难以在运行时发现难以调试的错误

    我们能做些什么呢? 我们可以为它们定义一个单独的类并将它们存储在其中。

    5.1首先,我们创建一个单独的javascript文件,其中包含在使用应用程序时需要更改的变量。

    Runtime.js

    5.2定义一个类来保存全局可用的数据,在这种情况下&#34; myLastCustomer&#34;变量

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

    5.3然后在整个应用程序中提供可用的序列

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

    5.4每当你想获取或设置全局变量值

    5.4.1设定值

                    MyApp.config.setMyLastCustomer(12345);
    

    5.4.2获取值

                    MyApp.config.getMyLastCustomer();
    

<强> 6。在组件中使用id是一个坏主意吗?

为什么?

6.1因为您定义的每个ID都应该是唯一的。在大型应用程序中,这可能会导致许多混淆和问题。

6.2很容易让框架处理组件的命名

                // 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'
                }]

在上面的示例中,有两个具有相同名称的按钮,这会导致名称冲突。    为防止它,请使用&#34; itemId&#34;而不是id。

                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’
                }]

现在,您可以按照下面的唯一名称访问上述组件

                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’);

<强> 7。不可靠的组件引用

使用组件定位来获取对组件的引用并不是一个好主意。因为有人可能会改变组件的位置   不知道它是通过在应用程序的另一部分中定位来引用的。

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

        var myWindow = myToolbar.ownerCt;

我们如何获得参考?     使用&#34; ComponentQuery&#34;或&#34; up&#34; /&#34; down&#34;方法

    var pictureSaveButton = Ext.ComponentQuery.query('#picturetoolbar > #savebutton')[0]; // Quering against the itemId
    var mySaveButton = myToolbar.down(‘#savebutton’);    // searching against itemId
    var myWindow = myToolbar.up(‘window’);

<强> 8。意图遵循大/小写命名惯例

将良好的命名约定作为最佳实践非常重要,因为它可以提高代码的一致性并使其易于阅读和理解。    同样重要的是为您定义的所有类,变量和方法使用有意义的名称。

BAD

       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);
            }
        });

GOOD

        Ext.define(‘MyApp.view.CustomerList’,{   // Use of capitalized and then camelCase
            extend : ‘Ext.grid.Panel’,
            alias : ‘widget.customerlist’,      // use of lowerCase
            myCustomConfig : ‘xyz’,             // Use of camelCase
            initComponent : function(){
                Ext.apply(this,{
                    store : ‘Customers’,
                    ….
                });
                this.callParent(arguments);
            }
        });

<强> 9。将组件约束到父组件布局。

BAD

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

&#34; MyGrid&#34;面板布局区域设置为&#34; center&#34;。因此,它不能在像#34; west&#34;这样的其他地区重复使用。    因此,重要的是以一种可以在任何问题上重复使用的方式定义您的组件。

BAD(??)

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


        Ext.create('MyApp.view.MyGrid',{
            region : 'center'   // specify the region when the component is created.
        });

还有另一种方法可以定义具有默认值的组件(在本例中为&#34; region&#34;属性)并在更改时覆盖默认值         是必需的默认值。

好的

       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
        });

<强> 10。使代码更加复杂。

有很多方法可以使简单的代码变得复杂。     其中一种方法是通过单独访问表单中的每个字段来加载表单数据。

BAD

    //  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’));

GOOD

    items : [{
        fieldLabel : ‘User’,
        name : ‘UserName’
    },{
        fieldLabel : ‘Email’,
        name : ‘Email’
    },{
        fieldLabel : ‘Home Address’,
        name : ‘Address’
    }];

    myForm.loadRecord(record);      // use of "loadRecord" to load the entire form at once.