Skip to main content

ExtJS4 Performance Tips

Ext JS has been getting more flexible with each release. You can do many more things with it these days than you used to be able to, but there has been a performance cost associated with that. In many cases this performance degradation is down to the way the framework is being used, as opposed to a fundamental problem with the framework itself.
There’s a whole bunch of things that you can do to dramatically speed up the performance of an app you’re not happy with, and Nige “Animal” White took us through them this morning. Here’s what I was able to write down in time:

Slow things

Nige identified three of the top causes of sluggish apps, which we’ll go through one by one:
  • Network latency
  • JS execution
  • Layout activity

Network latency:

  • Bad ux – got to stare at blank screen for a while
  • Use Sencha Command to build the app – single file, minimized
  • 4810ms vs 352ms = dynamic loading vs built

JavaScript execution:

  • Avoid slow JS engines (he says with a wry smile)
  • Optimize repeated code – for loops should be tight, cache variables outside
  • Ideally, don’t do any processing at render time
  • Minimize function calls
  • Lazily instantiate items
  • Use the PageAnalyzer (in the Ext JS SDK examples folder) to benchmark your applications
  • Start Chrome with –enable-benchmarking to get much more accurate timing information out of the browser

Layouts

Suspend store events when adding/removing many records. Otherwise we’re going to get a full Ext JS layout pass for each modification
1
2
3
4
grid.store.suspendEvents();
//do lots of updating
grid.store.resumeEvents();
grid.view.refresh()
Ditto on trees (they’re the same as grids)
Coalesce multiple layouts. If you’re adding/removing a bunch of Components in a single go, do it like this:
1
2
3
Ext.suspendLayouts();
//do a bunch of UI updates
Ext.resumeLayouts(true);
Container#add accepts an array of items, which is faster than iterating over that array yourself and calling .add for each one. Avoid layout constraints where possible – in box layouts, align: ‘stretchmax’ is slow because it has to do multiple layout runs. Avoid minHeight, maxHeight, minWidth, maxWidth if possible

At startup:

  • Embed initialization data inside the HTML if possible – avoids AJAX requests
  • Configure the entire layout in one shot using that data
  • Do not make multiple Ajax requests, and build the layout in response

Use the ‘idle’ event

  • Similar to the AnimationQueue
  • Ext.globalEvents.on(‘idle’, myFunction) – called once a big layout/repaint run has finished
  • Using the idle listener sometimes preferable to setTimeout(myFunction, 1), because it’s synchronous in the same repaint cycle. The setTimeout approach means the repaint happens, then your code is called. If your code itself requires a repaint, that means you’ll have 2 repaints in setTimeout vs 1 in on.(‘idle’)

Reduce layout depth

Big problem – overnesting. People very often do this with grids:
1
2
3
4
5
6
7
8
9
10
11
{
    xtype: 'tabpanel',
    items: [
        {
            title: 'Results',
            items: {
                xtype: 'grid'
            }
        }
    ]
}
Better:
1
2
3
4
5
6
7
{
    xtype: 'tabpanel',
    items: {
        title: 'Results',
        xtype: 'grid'
    }
}
This is important because redundant components still cost CPU and memory. Everything is a Component now – panel headers, icons, etc etc. Can be constructing more Components than you realize. Much more flexible, but easy to abuse

Lazy Instantiation

1
2
3
4
5
6
7
8
{
    xtype: 'tabpanel',
    ptype: 'lazyitems',
    items: {
        title: 'Results',
        xtype: 'grid'
    }
}

Overall impact

On a real life large example contributed by a Sencha customer:
Bad practices: 5187ms (IE8)
Good practices: 1813ms (IE8)
1300ms vs 550ms on Chrome (same example)
Colossal impact on the Ext.suspendLayout example – 4700ms vs 100ms on Chrome

Performance is a top-tier concern that has been carried over to my Ext JS (3 & 4) work.

  • Don't over nest. Most newbies nest panels several layers deep when a single panel will suffice.
  • Don't use a panel when a container will do. Likewise for other classes.
  • Don't gratuitously use a panel when some simple HTML or an Ext.Template will suffice.
  • Production builds should use concatenated, minified versions of all the source to improve load time.
  • Production builds should generally use a custom ExtJS build to reduce the file size.
  • Be wary of components or stores that don't get destroyed when they are discarded, memory can quickly leak.
  • Create abstracts. This cuts down on code duplication and bunch of other good things.
  • DOM is the slowest thing ever! There is a trade-off with cute "features" and performance. If you have a TabPanel or Card layout, use deferred rendering. I go as far as to destroy cards (or remove all items) that aren't visible.
  • Code smart... there are tons of tricks to accomplish the same thing but with more efficient code.

Less Is More


Most of the ExtJS applications I've seen suffer from panel explosion. Everything is done using a panel, or to be more accurate, everything is done using several panels.

There are alternatives.

The first problem is over-nesting. Panels within panels within panels, when one panel would suffice. Often people don't even realize they're doing it.

The next problem is using a panel when there are much more lightweight alternatives. Everyone loves panels but truth be told they're overkill for most parts of a UI. I once saw a UI that displayed a few dozen thumbnail images and each image was rendered as the HTML content of a different panel. All those panel were totally unnecessary. Consider instead
1.     Could you use HTML and CSS instead of a full component? An Ext.Template or Ext.XTemplatemight prove helpful.
2.     Could you just use a custom Ext.Component? There are various config settings for injecting HTML into a component: autoElhtmltpl & data.
3.     Could you use a DataView to render all of your data instead of a number of individual components?
4.     Could you use an Ext.container.Container rather than an Ext.panel.Panel to reduce the overhead?

The more panels (and components in general) you create, the more stress it will put on the layout system and the larger the DOM will be.

Component Render time Fiddle:

http://jsfiddle.net/prajavk/7bKQn/

Handler Functions

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.

Consider this example:
Ext.define('MyClass', {
    ...

    constructor: function(config) {
        ...

        this.store.on('load', this.onStoreLoad, this);
    },

    onStoreLoad: function() {
        ...
    }
});

For completeness, I should point out that my use of on is probably misguided here. Though there are other factors to consider, it would usually be better to write it using mon instead:
this.mon(this.store, 'load', this.onStoreLoad, this);

This will ensure that the listener is automatically removed when instances of MyClass are destroyed.

xtypes


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.


Load Times


To improve load times in production builds:

1.     Do not use Ext.Loader.
2.     All JS files should be concatenated and minified. Even on a fast internal network this can make a huge difference.
3.     All CSS files should be concatenated and minified.
4.     Consider building a custom version of ext-all.js. Are you really using the whole library?
5.     If you use a lot of images then image sprites can help reduce the number of requests and also provide an implicit form of pre-caching for the other images in the sprite.

Disabling Animations


It can help to improve UI snapiness if you disable animations. This is something you could do selectively such that faster browsers have the animations but slower browsers don't.

Different Settings For Different Browsers


Whilst you may be required to support older versions of IE, it isn't necessarily the case that you need to provide an identical UI to the one you offer in newer, faster browsers.

Are there things you could cut in older browsers to help performance? Animations and visual effects are an obvious example but there are others. What about the page size of your grids? It might help to reduce it in older browsers. Many UIs have redundancy built in to make them more convenient, e.g. 3 different ways to do the same thing.

Do you make a custom build via the "builder" provided in the ExtJs package?

Yes. This can help load times but probably won't improve UI snapiness. I wouldn't do this until your app has matured a bit, working with a cut down build gets really annoying when you need to use the bits you've cut out.

How would I make sure they are destroyed?
When a component is created it is registered with the component manager. The component's destroy()method will, among many other things, unregister it with the manager. This is important because the JS garbage collector can't reclaim a component if there's still a reference to it from the manager.

You can see all the components in your app using Ext.ComponentManager.all.

ExtJS does what it can to destroy components for you. When you destroy a container it will destroy all of its children for you. When you remove a component from a container it will, by default, be destroyed. When a window or tab is closed the default is to destroy it.

Recommendation:

Do not use iFrames, but go for a single page app instead.

Regarding the memory leaks: loading a blank page into the iframe will remove the DOM for sure, but not the JS objects / instances you created unless you take care about it. Check the Chrome developer tools to spot out what is leaking.

You can create a custom build of the Ext JS library in case you are using MVC:
http://docs.sencha.com/extjs/4.2.1/#...etting_started

Grid and Stores:

In case you create a grid using a store and destroy the grid, it does not mean that the store automatically gets destroyed as well.

This is why I recommended to check for the amount of store instances, because in case you keep them including the data it would explain the leaks.

Take a look at the autoDestroy config for stores:
http://docs.sencha.com/extjs/4.2.1/#...fg-autoDestroy


Most components clean up everything in a good way, but what i was talking about are the things users create on their own. Let's say you extend panel as a class, use the initComponent method to create children there and stores and save them into references of the class, then you need to remove them.

You can always (for every extension of component) use the onDestroy method.

Here as an example what we do for the ComboBox:

Code:
    onDestroy: function() {
        Ext.destroy(this.listKeyNav);
        this.bindStore(null);
        this.callParent();
    }


I have extended Ext.data.Strore as follows
Ext.define('My.grid.Store', {
extend: 'Ext.data.Store',

and I implemented 
onDestroy: function() {
alert("In store.onDestory() function");
this.callParent();
}

References:

Comments

Popular posts from this blog

ExtJS - Grid panel features

What can we do with ExtJS GridPanel? I have to develop a lot of applications in my web app and I see that grid component of ExtJS may fit in. However, I am not aware of what all things I can do with the - off the shelf available framework pieces - available plug-ins in the marketplace and - custom development through my own developers This is a typical question that we hear from the business users who wants to design an application by keeping the framework’s capability in perspective. In this article I have tried to put the list of stuff you can do with grid and hopefully that shall enable you to take advantage of the beauty of ExtJS. Pre-requisites This article assumes that you are familiar with basics of ExtJS What are the available options? In this section I will be taking you through some of the commonly seen usage of ExtJS grid panel. While covering all the capabilities of grid may not be possible, I am sure it will be helpful for the business users who want to...

ExtJS 4 with Spring MVC Example

Introduction When developing a brand new application for the company I work for, one of the first thing I implement is "authentication". Not only is this process generally a prerequisite to access many other functions but it is also very simple and covers every layer of an application, from GUI to the database. So instead of another minimalistic "Hello World" tutorial, here is a "Login/Password" example, using 2 famous technologies : Spring MVC and ExtJS 4.   Prerequisites Because this tutorial is quite long, I won't go into the details of each technology I used but I will mainly focus on how ExtJS and Spring can be nicely used together. So here are the things you should know before continuing : Spring framework (good knowledge) Spring MVC (basic knowledge. See  Spring MVC Official Documentation  for details) ExtJS "application architecture" (see  Sencha Docs  for details) Eclipse IDE + Tomcat 6 (or any other web container) You a...

Ext4 Apply Store Filtering

In extjs4.1: There are many way for store filtering . Some of code i give here Filtering by single field: store . filter ( 'eyeColor' , 'Brown' );   Alternatively, if filters are configured with an  id , then existing filters store may be  replaced by new filters having the same  id . Filtering by single field: store . filter ( "email" , /\.com$/ );   Using multiple filters: store . filter ([ { property : "email" , value : /\.com$/ }, { filterFn : function ( item ) { return item . get ( "age" ) > 10 ; }} ]);   Using  Ext.util.Filter  instances instead of config objects (note that we need to specify the root config option in this case): store . filter ([ Ext . create ( ' Ext.util.Filter ' , {   property : "email" , value : /\.com$/ , root : 'data' }),   Ext . create ( ' Ext.util.Filter ' , {   filterFn : function ( item ) {   return item . get ( ...