ExtJs best practices

后端 未结 3 902
谎友^
谎友^ 2021-02-04 19:42

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 layou

相关标签:
3条回答
  • 2021-02-04 20:25

    Sencha blog article about top ten worst practices is worth reading.

    Sencha Top 10 Worst Practices

    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

    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. Moreover other elements like forms, trees, tab panels are extension of panel.

    2. Memory leaks caused by failure to cleanup unused components.

    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.

    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

    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)

    VIEWS (grids, forms,...etc)

    In ExtJS controllers can talk to each other.

        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.

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

    4. Poor folder structure for source code

    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

    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

      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.

      Runtime.js

      5.2 Define a class to hold the globlly available data, in this case "myLastCustomer" variable

                 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

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

      5.4 Whenever you want to GET or SET the global variable value

      5.4.1 To SET value

                      MyApp.config.setMyLastCustomer(12345);
      

      5.4.2 To GET value

                      MyApp.config.getMyLastCustomer();
      

    6. Use of ids in components is a bad idea?

    Why?

    6.1 Because every id that you define should be unique. 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

                    // 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.

                    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

    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.

        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

    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.

    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". 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.

    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.

    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. 
    
    0 讨论(0)
  • 2021-02-04 20:30

    Useful links:

    • API documentation - really useful
    • Tutorials - official tuts
    • Screencasts - by real-world ExtJS users
    • Community manual
    • ExtJS override
    • ExtJS forum - user apps thread
    0 讨论(0)
  • 2021-02-04 20:36

    In my company main code reviewer enforcing:

    • As component always talk to PAPA - never to you sibling (own children offcourse allowed)
    • Try to avoid bubblling
    • If you follow #1 you do not have need to use Ext.getCmp () it too expensive, so DO NOT
    • 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... :)

    0 讨论(0)
提交回复
热议问题