Friday, September 6, 2013

Efficient coding style in ExtJs: Volume 5


Very important links related to this post:

33) Do not create inline handler functions: Never create inline handler function because The functions used for callbacks and listeners are a common source of problems. They not only take time to create but can also form expensive closures. E.g.

Ext.define(‘MyClass’,{
                constructor: function(config){
                                this.store.on(‘load’, function(){
                ………..;
}, this);
                }
});

The thing to realize is that a new listener function is created each time this constructor runs. This not only takes time but could potentially leak loads of memory though a closure. The preferred pattern looks like this:

Ext.define(‘MyClass’,{
                constructor: function(config){
                                this.store.on(‘load’, this.onStoreLoad, this);
                },
                onStoreLoad: function(){
                                ………;
}
});
In this case the listener function is created once and stored on the class. Each instance will share the same function. The overhead for creating the function is incurred just once and there's no risk of a leaky closure. Even better, this style of coding makes it easy for subclasses to change the listener with a simple override.

34) Proper use of xtype: A common myth is that xtypes provide a performance boost through lazy-instantiation. In general this is not true. As soon as you add an xtype-config to a container it will instantiate the full component, even if it isn't visible.
Whilst xtypes have many great uses they usually have no measurable impact upon application performance or resource consumption.

35) Never use whole Ext-all.js library: Generally Ext-all.js contains huge set of coding for supporting all features and components and we seldom use all features and components of ExtJs. So, it is unnecessary to load such a huge file when we are using its only 60-70% codes. If we minify our production code with the help of sencha command tool, it will automatically copy only required classes from Ext-all.js and paste them in resulting all-classes.js. If we are not using sencha command tool, we should make sure by any other means that we are not loading whole Ext-all.js file.

36) Batching tasks: Try to batch changes that cause re-rendering so that the rendering only happens once at the end. For example, changing a field value in a record will immediately update the grid. If you want to update several fields it's more efficient to batch them: e.g. 

record.beginEdit();
record.set(‘name’, ‘Tom’);
record.set(‘age’, 17);
record.set(‘member’, true);
record.endEdit();

The more fields you change the more noticeable the effect will be.
Another example is adding components to a container. The container will recalculate its layout every time add() is called. Passing multiple items to the same add() call will be much more efficient. E.g.

//slow code
container.add(panel);
container.add(button);
container.add(grid);

//fast
container.add(panel, button, grid);

//using an array is also fast
container.add([panel, button, grid]);

In cases where no other batching mechanism exists it can help to suspend layouts temporarily:

container.suspendLayout = true;
doSomethingToChangeLotsOfLayoutChange();
container.suspendLayout = false;
container.doLayout();

37) Use deferredRender: When using a card layout or tab panel, be careful to consider the deferredRender option. When set totrue it will cause the hidden cards/tabs to be lazily rendered. Rendering will only occur when the hidden items are first shown. When set to false, all cards/tabs will be rendered at the same time, as part of the rendering of the container.
Usually it is better to defer rendering as it helps to break the rendering process up into smaller chunks that won't cause a noticeable delay to the user. However, rendering all the cards/tabs up front can help to improve responsiveness of the UI when the hidden cards/tabs are first shown.

38) Buffered rendering of Grid: When using a grid, if you have a large data set, use buffered rendering.This means that the Store keeps a "prefetch" buffer containing a page of Records which is used when possible to load the Store.
As long as the data set is less than maybe 100,000 rows ( also depends on number of columns in each rows), it's probably best to keep all the data in the client in this prefetch buffer.
It's the rendering of a large HTML table that kills performance. That's just a fact of the HTML table algorithms. Just holding all the data should not cause problems.
What we can do is do "just in time" rendering of the rows in the table as we scroll. To do this, first load the prefetch buffer with all the data. And when that is done, you can then load the Store's main data cache from the prefetch e.g.

myStore = ……{
………….,
Buffered: true, // Tell grid to use a paging scroll manager to scroll and refresh table automatically.
                                perPageCount: 0 // never remove pages from the prefetch buffer;
}

myStore.prefetch({
                                start: 0,
                                limit: 999999,
                                callback: function(){
                                myStore.load(0, 49);
}
});

What that does is prefetches what is hopefully the whole data set. Then the callback loads the Store's main cache (the cache that is mapped to a <table> within the GridPanel) with the first 50 rows. So we only get a small 50 row table. As it scrolls nearly out of view, it is refreshed. This technique works OK in 4.0.x, but it has been really overhauled in 4.1.0 and will be extremely fast and smooth. The buffer-grid example in the grid directory illustrates this technique.
Be aware that it seems that a large number of columns has an effect on the performance of HTML <table> elements too, so avoid columns which do not really need to be present.

39) Populate each tab in tab panel in their beforRender event handler: It is better to populate the tabs in their beforeRender methods. Done correctly it can be very effective at improving performance in applications that have a lot of unrendered components. Although, the time taken to instantiate a component is usually much smaller than the time taken for rendering or layout but if you have huge numbers of unrendered components then it can add up. A tabpanel with many tabs is a good candidate. It should be noted that all I'm proposing is to shuffle time around: cut the time taken for the initial load in favour of slightly slower tab transitions. It's just another technique to have in your arsenal. As ever, the only golden rule for optimizing is to take some meaningful measurements before you do anything else.

40) Use ‘mon’ instead of ‘on’: Both of these keywords are used to register an event handler to its corresponding events. But its better and efficient to use ‘mon’ instead of ‘on’ because This will ensure that the listener is automatically removed when instances of MyClass are destroyed. E.g.

Ext.define(‘MyClass’,{
                                Constructor: function(config){
                                                //this.store.on(‘load’, this.onStoreLoad, this); // this is not good
                                                this.store.mon(‘load’, this.onStoreLoad, this);
                                },
                                onStoreLoad: function(){
                                                ………;
}

});

No comments:

Post a Comment

Please provide your precious comments and suggestion