ExtJs最佳实践

6
似乎大多数ExtJS书籍和介绍都只关注展示所有美好的特性,但是它们中的大部分并没有真正解释如何使用ExtJS构建稳定、可维护的布局/应用程序。如何编写代码而不会创建难以阅读的混乱代码……我没有具体的应用程序想法,我只是想知道如何“美化”我的ExtJS代码。因此,如果需要示例,则针对项目管理(图书馆)或类似招聘板的“常规”应用程序最能描述我所思考的内容。那么,有谁可以分享一些关于如何在客户端代码中使用ExtJS构建这些应用程序的良好链接或建议吗?谢谢您的帮助,祝您好运!

1
我总是尝试在事件中完成所有操作,而不是在ExtJs之外的函数中完成。 - devOp
3个回答

8
在我的公司,主要的代码审查人员强制执行以下规则:
  • 组件始终与PAPA通信 - 而不是与您的同级(当然可以使用自己的子代)
  • 尽量避免冒泡
  • 如果您遵循第1条,就没有必要使用Ext.getCmp(),因为它太昂贵了,所以不要使用
  • 考虑每个组件都可以由您团队中的其他人重用
  • 在命名空间中使用适当的层次结构(并使用命名空间)
这些只是一些简单的规则,需要遵循文档。 :)

1
为什么在ExtJS中应该避免冒泡? - shabunc

3

Sencha博客中关于最糟糕的十个做法值得一读。

Sencha最糟糕的十个做法

以下是该博客文章的概要,请注意所有权归原博客作者所有。

1. 过度或不必要的组件结构嵌套

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

错误示例

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

好的

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

在上面的例子中,嵌套面板是多余的,因为网格是面板的扩展。
此外,像表单、树、选项卡面板这样的其他元素也是面板的扩展。
2. 由未清理未使用的组件导致的内存泄漏。
这是有史以来最重要的规则之一。在任何编程语言中,非常重要的是要确保不再使用的组件被正确地丢弃,即使在像Java这样的语言中,GC正在为我们执行所有清理操作,我们也应该确保我们在完成后没有持有任何对象。
错误示例:
    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());
 
        }
    });

每当用户右键单击网格行时,都会创建一个新的上下文菜单。这看起来还不错,因为我们只能看到最新的菜单。 不好(??)
    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());
        }
    });
    

这个版本比最初的更好。当用户右键单击网格视图时,它每次都使用相同的菜单对象。但即使我们关闭了网格视图,它仍会保持菜单处于打开状态,这不是我们需要的。

好的做法:

    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. 控制器怪物
有些人写的代码就像怪物一样...开个玩笑,但是有些大型控制器(不仅仅是控制器,其他组件也是 :))由数千行代码组成,执行那些完全没有关系的操作。
在项目开始阶段找到一种方式将您的应用程序分解为不同的处理单元非常重要,这样您就不会得到一个处理应用程序中所有进程的巨大控制器。
建议:通过不同的方式来拆分您的应用程序
- 应用程序功能(在订单处理应用程序中 - >下订单,交付,客户查找等) - 视图(网格,表单等)
在ExtJS中,控制器可以彼此交流。
    this.getController('SomeOtherController').runSomeFunction(myParm);
            

也可以触发一个应用程序级别的事件,任何控制器都可以监听。

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

另外一个控制器监听应用程序级事件。
MyApp.getApplication().on({
    myevent : doSomething
});

4. 源代码的文件夹结构不佳

在任何应用程序中,良好的结构都非常重要,因为它提高了项目的可读性和可维护性。

与其将所有控制器放在一个文件夹中,将所有视图放在另一个文件夹中,不如根据它们的功能进行逻辑上的结构化。

5. 使用全局变量

为什么使用全局变量是不好的?

有时候它所持有的实际值并不清楚,因此可能会导致很多混淆,例如:

  • 名称冲突
  • 难以找到运行时的错误,这些错误很难调试

我们该怎么做呢?

我们可以为它们定义一个单独的类并将它们存储在其中。

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

Runtime.js

5.2 定义一个类来保存全局可用的数据,在这种情况下是“myLastCustomer”变量。

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

在上面的示例中,有两个具有相同名称的按钮,这会导致名称冲突。
为了避免这种情况,请使用"itemId"代替"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;
        

我们如何获取参考?

使用“ComponentQuery”或“up”/“down”方法。

    var pictureSaveButton = Ext.ComponentQuery.query('#picturetoolbar > #savebutton')[0]; // 

针对itemId进行查询

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

8. 未能遵循大小写命名规范

使用良好的命名规范是最佳实践,因为它可以提高代码的一致性并使其易于阅读和理解。

此外,对于您定义的所有类、变量和方法,使用有意义的名称也非常重要。

错误示例:

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

好的

        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. 将组件限制在父组件的布局中 不好的做法:"
       Ext.define('MyApp.view.MyGrid',{
            extend : 'Ext.grid.Panel',
            initComponent : function(){
                Ext.apply(this,{
                    store : ‘MyStore’,
                    region : 'center',
                    ......
                });
                this.callParent(arguments);
            }
        });
        

"MyGrid"面板布局区域被设置为“中心(center)”。因此,它不能在其他区域(如“西部(west)”)中重复使用。
因此,定义组件的方式非常重要,以便可以在任何问题下重复使用。
不好的示例: 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.
        });
    
    

有另一种定义带默认值组件的方法(在这种情况下,“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
        });

10. 让你的代码比必要复杂

有许多方法可以使简单的代码变得复杂。

其中一种方式是通过逐个访问表单中的每个字段来加载表单数据。

不好的例子:

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

好的

    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. 

3

1
好吧,确实有一个教程是针对我所说的方向的(http://www.sencha.com/learn/Tutorial:Writing_a_Big_Application_in_Ext)...还是谢谢你们没有考虑我的问题:( - pagid
看起来你错过了最后一个链接(至少)- http://www.sencha.com/forum/showthread.php?71530-TomatoCart-online-shopping-cart-solution-with-ExtJS-administration-site http://www.sencha.com/forum/showthread.php?107636-Project-Wow-Rapid-Web-Application-Development-with-ExtJS http://www.sencha.com/forum/showthread.php?118092-Complete-Application http://www.sencha.com/forum/showthread.php?26585-Web-Based-EMail-Client-written-with-Ext 下次最好仔细看一下 :-) - Kamil Tomšík
@KamilTomšík 我也有同样的疑问,如果你有聊天权限,请查看此链接:http://chat.stackoverflow.com/rooms/7451/extjs-3-x-4-x-java-script-framework - user976244
最好将必要的部分包含在答案本身中,而不是仅提供没有任何解释的链接。-1 - Xatenev

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接