简体   繁体   English

ExtJs最佳实践

[英]ExtJs best practices

it seems that most ExtJS books and ExtJS introductions just focus on showing all the nice features but most of them don't really explain how to build stable, maintainable layouts / applications with ExtJS. 似乎大多数ExtJS书籍和ExtJS介绍只关注显示所有不错的功能,但大多数并没有真正解释如何使用ExtJS构建稳定,可维护的布局/应用程序。 And how to write them without creating an unreadable mess... 如何写它们而不会造成难以理解的混乱......

I've no specific application in mind, I'm just wondering how I can "beatify" my ExtJS code. 我没有特定的应用程序,我只是想知道如何“美化”我的ExtJS代码。 So if you need an example, a "regular" application for item management (library) or something like a job board would best describe what I was thinking of. 因此,如果您需要一个示例,项目管理(库)或类似工作板的“常规”应用程序将最好地描述我的想法。

So can anyone share some good links or advices on how to structure such applications when I want to build them with ExtJS for the client side code? 因此,当我想使用ExtJS为客户端代码构建这些应用程序时,任何人都可以分享一些关于如何构建这些应用程序的良好链接或建议吗?

Thanks in advance, Cheers 提前谢谢,干杯

In my company main code reviewer enforcing: 在我的公司主要代码审查员执行:

  • As component always talk to PAPA - never to you sibling (own children offcourse allowed) 作为组件总是与PAPA交谈 - 永远不会给你兄弟姐妹(允许自己的孩子离开)
  • Try to avoid bubblling 尽量避免冒泡
  • If you follow #1 you do not have need to use Ext.getCmp () it too expensive, so DO NOT 如果你遵循#1你不需要使用Ext.getCmp()它太贵了,所以不要
  • Consider each component reusable by someone else in your team 考虑团队中其他人可重用的每个组件
  • Use proper hierarchy in namespaces (and use namespaces) 在命名空间中使用适当的层次结构(并使用命名空间)

As only a few main rules on top of follow documentation... :) 作为遵循文档的几个主要规则...... :)

Sencha blog article about top ten worst practices is worth reading. 关于十大最差实践的Sencha博客文章值得一读。

Sencha Top 10 Worst Practices Sencha十大最糟糕的做法

A Summary from the blog post 博客文章摘要

**Please note that, all credit go to rightful owners of the original blog post. **请注意,所有信用均归原始博文的合法所有者所有。

1. Excessive or unnecessary nesting of component structures 1.组件结构的过度或不必要的嵌套

Sometimes developers use redundant nesting components, which could result in unexpected unappealing aesthetics in the app with oddities like double borders or unexpected layout behavior. 有时开发人员使用冗余嵌套组件,这可能会在应用程序中产生意外的不吸引人的美感,例如双边框或意外的布局行为。

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 : [{...}]
        }]

In the above example the nested panel is redundant because grid is an extension of panel. 在上面的示例中,嵌套面板是冗余的,因为grid是面板的扩展。 Moreover other elements like forms, trees, tab panels are extension of panel. 此外,其他元素如形状,树木,标签面板是面板的延伸。

2. Memory leaks caused by failure to cleanup unused components. 2.由于未清除未使用的组件而导致的内存泄漏。

This is one of the most important rules of all time. 这是有史以来最重要的规则之一。 In any programming language it is very very important to make sure that components which are no longer in use are discarded properly, even in languages like Java, where GC is doing all cleanup for us, we should make sure that we are not holding to any objects after we are done with them. 在任何编程语言中,确保不再使用的组件被正确丢弃是非常非常重要的,即使在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());

        }
    });

Every time user right clicks on a grid row, a new context menu is created. 每次用户右键单击网格行时,都会创建一个新的上下文菜单。 Which looks ok, because we only see the latest menu. 哪个看起来不错,因为我们只看到最新的菜单。

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

This is some what better than the initial one. 这比初始的要好一些。 It uses the same menu object everytime when user right clicks on a grid view. 每当用户右键单击网格视图时,它就会使用相同的菜单对象。 However it will keep the menu alive even if we kill the grid view, which is not what we need. 但是,即使我们杀死网格视图,它也会使菜单保持活动状态,这不是我们需要的。

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

In the above view, when the grid is destroyed, we destroy the menu as well. 在上面的视图中,当网格被销毁时,我们也会破坏菜单。

3. Monster controllers 3.怪物控制器

Some people code like monsters... Just kidding, but there are some big controllers(Not just controllers, other components as well :)) that consist of thousands of lines of code doing all those stuff that have no relation to each other at all. 有些人像怪物一样编码......开玩笑,但是有一些大型控制器(不只是控制器,其他组件也是如此:))由数千行代码组成,完成所有那些根本没有关系的东西。

It is very important to find a way to break down your application into different processing units at the beginning of the project so that you won't end up with a giant controller that handles all processes in your application. 在项目开始时找到一种将应用程序分解为不同处理单元的方法非常重要,这样您就不会得到一个处理应用程序中所有进程的巨型控制器。

Suggestion: 建议:

Breakup your application by different 通过不同的方式分解你的申请

APP FUNCTIONS (In an Order processing app --> Ordering, Delivering, Customer lookup...etc) APP功能(在订单处理应用程序中 - >订购,交付,客户查找等)

VIEWS (grids, forms,...etc) 视图(网格,表格等)

In ExtJS controllers can talk to each other. 在ExtJS中,控制器可以相互通信。

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

Also possible to fire an application level event that any controller can listen for. 也可以触发任何控制器可以侦听的应用程序级事件。

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

Also another controller listens for the app-level event. 另一个控制器监听app级事件。

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

4. Poor folder structure for source code 4.源代码的文件夹结构不佳

In any application good structure is very important, because it improves the readability and maintainability of a project. 在任何应用程序中,良好的结构都非常重要,因为它提高了项目的可读性和可维护性。 Instead of putting all controllers in one folder and all views in another folder, it is better to structure them logically according to their function. 不是将所有控制器放在一个文件夹中,而是将所有视图放在另一个文件夹中,最好根据它们的功能逻辑地构造它们。

5. Use of global variables 5.使用全局变量

Why it is bad to use global variables? 为什么使用全局变量是不好的? Sometimes it is not clear the actual value it holds, therefore it might lead to lot of confusions like 有时它不清楚它所拥有的实际价值,因此它可能导致许多混淆,如

  • Name collisions 名称冲突
  • Hard to find bugs at runtime which are difficult to debug 很难在运行时发现很难调试的bug

    What can we do about it? 我们对于它可以做些什么呢? We could define a separate class for them and store them in it. 我们可以为它们定义一个单独的类并将它们存储在其中。

    5.1 First we create a separate javascript file which holds the variables that needs to be changed as the app is used. 5.1首先,我们创建一个单独的javascript文件,其中包含在使用应用程序时需要更改的变量。

    Runtime.js Runtime.js

    5.2 Define a class to hold the globlly available data, in this case "myLastCustomer" variable 5.2定义一个类来保存全局可用数据,在本例中为“myLastCustomer”变量

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

    5.3 Then make the veriables available throughout the application 5.3然后在整个申请过程中提供可用的变量

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

    5.4 Whenever you want to GET or SET the global variable value 5.4每当你想获取或设置全局变量值时

    5.4.1 To SET value 5.4.1设置值

      MyApp.config.setMyLastCustomer(12345); 

    5.4.2 To GET value 5.4.2获取价值

      MyApp.config.getMyLastCustomer(); 

6. Use of ids in components is a bad idea? 6.在组件中使用id是个坏主意?

Why? 为什么?

6.1 Because every id that you define should be unique. 6.1因为您定义的每个id都应该是唯一的。 In a large application this could lead to lot of confusions and problems. 在大型应用程序中,这可能会导致许多混淆和问题。

6.2 It is easy to let the framework handles the naming of components 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'
                }]

In the above sample, there are two buttons with the same name, which leads to name collisions. 在上面的示例中,有两个具有相同名称的按钮,这会导致名称冲突。 To prevent it, use "itemId" instead of id. 要防止它,请使用“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’
                }]

Now you can access the above components by their unique names as below 现在,您可以按照下面的唯一名称访问上述组件

                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. Unreliable referencing of components 7.组件的不可靠引用

It is not a good idea to use component positioning to get a reference to a component. 使用组件定位来获取对组件的引用不是一个好主意。 Because someone might change the positon of a component without knowing that it is referenced by positioning in another part of the application. 因为某人可能会更改组件的位置而不知道它是通过定位在应用程序的另一部分中而引用的。

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

        var myWindow = myToolbar.ownerCt;

How we can get the reference? 我们如何获得参考? Use the "ComponentQuery" or "up" / "down" methods. 使用“ComponentQuery”或“up”/“down”方法。

    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. Failling to follow upper/lower case naming conventions 8.愿意遵循大/小写命名惯例

It is important to use good naming conventions as a best practice, because it improves the consistance of your code and make it easy to read and understand. 使用良好的命名约定作为最佳实践非常重要,因为它可以提高代码的一致性并使其易于阅读和理解。 Also it is important to use meaningful names for all classes, variables and methods you define. 同样重要的是为您定义的所有类,变量和方法使用有意义的名称。

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. Constraining a component to a parent components layout. 9.将组件约束到父组件布局。

BAD

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

The "MyGrid" panel layout region is set as "center". “MyGrid”面板布局区域设置为“中心”。 Therefore it cannot be reused in another region like "west". 因此它不能在“西方”等其他地区重复使用。 So it is important to define your componets in such a way that it can be reused with any problem. 因此,重要的是以一种可以在任何问题中重复使用的方式定义您的组件。

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

There is another way to define a component with defaults(in this case, the "region" property) and override the defaults when ever a change is required to defaults. 还有另一种方法可以定义具有默认值的组件(在本例中为“region”属性),并在默认值需要更改时覆盖默认值。

GOOD

       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. Making your code more complicated than necessary. 10.使代码更加复杂。

There are many ways to make a simple code complicated. 有许多方法可以使简单的代码变得复杂。 One of the many ways is to load form data by accessing each field in the form individually. 许多方法之一是通过单独访问表单中的每个字段来加载表单数据。

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. 

Useful links: 有用的链接:

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM