Blog

Is your Grid too slow? You are probably using the wrong renderer

By  
Gilberto Torrezan Filho
Gilberto Torrezan Filho
·
On Sep 26, 2019 12:32:00 PM
·

 

If you are building a business web application with Vaadin, chances are that you are using Vaadin Grid. 

There are multiple ways to create columns with Grid: some are more flexible than others. But what you might not  know is how they differ in terms of performance on the client side.

Why Client-Side Performance Matters

Performance is easy to understand but hard to define. Is a Grid that takes a second to load fast enough? Or is it too slow? A slow performing app can jeopardize your business, while a fast app can give you the edge you need over competitors with similar feature sets.

At the end of the day, "performance" depends on your product, your market, and your customers. However, as developers we always want to have the fastest implementation available, as long as it doesn't hurt maintainability and productivity.

Luckily enough, it's easy to understand and improve your Grid, so it can perform as fast as it can be. In this post, I'll categorize 3 ways of building Grid columns, from the slowest to the fastest, so you can adapt your code to find the best balance between performance and versatility. Here's an overview:

  	grid.addComponentColumn(item -> new Span(item.getName())); // slow
    grid.addColumn(TemplateRenderer.of("[[item.name]]").withProperty("name", item -> item.getName())); // fast
    grid.addColumn(item -> item.getName()); // fastest
	

ComponentRenderers: Easy to Build, Slow to Render

If you have something like this in your code:

  	grid.addComponentColumn(item -> new AnyComponent());
	

... you are using ComponentRenderers. ComponentRenderers take each item in the DataProvider and create a component instance to represent the value of the item in that column. Each component can be fully controlled from the server side, like adding listeners, changing its properties, and so on. You have full control over the rendered component and you can use multiple components bundled together in a layout, for example:

  	grid.addComponentColumn(item -> {
        VerticalLayout layout = new VerticalLayout();
        layout.add(new Span(item.getName()));
        Button button = new Button("Update");
        button.addClickListener(e -> update(item));
        layout.add(button);
        return layout;
    });
	

Why Is It Slow?

For each rendered cell, Grid needs to create a component instance on the server side, render it on the client side, and hook things up to let the server side keep track of the component status, so it can update its state and receive events. For example, if you have a Grid with 100 items and 10 columns, that adds up to 1000 components to manage already. And it gets worse if you have layouts and other components to manage.

All in all, ComponentRenderers are very flexible and easy to use, but you should use them with caution. They are better-suited for editors (since you can only edit a single row at a time) or details rows.

TemplateRenderers: Flexible and Fast, if You Can Get Your Hands Dirty with HTML

TemplateRenderers use the native <template> support in the vaadin-grid web component: for each cell, it stamps the template with the correct bindings on the client side. It's fast and relatively flexible, if you know how to deal with HTML.

The downside is that you cannot use pure components from the server side, unless you know how to represent them in HTML format. This also means that you cannot manage the state of the elements from the server side - the template is immutable. It can have different representations depending on the state of the object, but the state comes from the object, not from the element.

Take a look at this example:

  	grid.addColumn(TemplateRenderer.<Item> of(
           "<vaadin-vertical-layout>"
           + "<span>[[item.name]]</span>"
           + "<vaadin-button on-click='update'>Update</vaadin-button>"
           + "</vaadin-vertical-layout>")
           .withProperty("name", item -> item.getName())
           .withEventHandler("update", item -> update(item)));
	

With this approach, the only data sent from the server (other than the template itself, which is only sent once) is the extra name property of each item. Note that even with templates, you can still have event handling on the server side - but you can't just, for example, disable or change the text of a button from the event handler. For this type of things, I recommend using editors instead.

Why is It Fast?

With TemplateRenderers, the server doesn't have to keep track of the components in each cell. It only manages the state of the item object in each row, which is something it needs to do anyway (via DataProviders). Also the client side doesn't need to wait for the server to send missing information about what needs to be rendered: it can use the template and stamp away all the information it needs.

ValueProviders: The Fastest, but Usable Only for Simple Values

Say you build a column by simply returning the value that should be presented in the cells, like:

  	grid.addColumn(item -> item.getName()); // or grid.addColumn(Item::getName);
	

In this case, you are using ValueProviders. ValueProviders are just that: they provide values for a given item in a particular column. It's the fastest way to represent data that doesn't require complex layouting or styling. Do you want a String or a number in your Grid? That's how you do it.

Oh, and returning HTML as a value will get your HTML escaped in the client side. If you want to render specific HTML for a cell, use TemplateRenderers.

Why Is It the Fastest?

This is the fastest option because not even the template stamping is used in this case. The value is used directly in the web component, and the performance is perceptible in lower-end devices and browsers that require polyfills in order to have support for <template> (such as IE11). For regular devices with evergreen browsers, the difference between ValueProviders and TemplateRenderers is not meaningful.

Enough Talk, Show Me Some Numbers!

Let's get some numbers. Here is the sample code I'm running to collect execution times:

(This code can be copy-pasted to the MainView of an empty project (Plain Java servlet) from vaadin.com/start)

  	@Route("")
    public class MainView extends VerticalLayout {

       public MainView() {
           final int numberOfItems = 100;
           final int numberOfColumns = 100;

           List<String> items = IntStream.range(0, numberOfItems)
                   .mapToObj(index -> "Item " + index)
                   .collect(Collectors.toList());

           Grid<String> grid = new Grid<>();
           grid.setItems(items);

           for (int i = 0; i < numberOfColumns; i++) {
               // ComponentRenderer
               grid.addComponentColumn(item -> new Span(item));

               // TemplateRenderer
               grid.addColumn(TemplateRenderer.<String> of("[[item.string]]")
                       .withProperty("string", item -> item));

               // ValueProvider
               grid.addColumn(ValueProvider.identity()); // same as item -> item
           }

           grid.setWidth("800px");
           grid.setHeight("600px");
           add(grid);
       }
    }
	

(Comment out the tests that are not relevant for each run of the experiment)

As for the measurements, keep in mind that what is important is not the absolute value of the measurements, but the difference between each approach. The actual number varies a lot from machine to machine, so my numbers can be very different from yours.

So, running in my Chrome 76 with Grid in Vaadin 14.0.0, I got:

  • ComponentRenderer: 4.41s to render
  • TemplateRenderer: 1.84s to render
  • ValueProvider: 1.51s to render

As expected, ComponentRenderer was very slow compared to the other ones, while TemplateRenderer and ValueProvider were very close to each other.

Now, let's test with IE11. While the browser is starting up, let me grab a cup of tea.

... oh, it's still loading. I have time for another one.

... there you go! Let's run the tests.

  • ComponentRenderer: 2min 20s to render
  • TemplateRenderer: 2min 7s to render
  • ValueProvider: 5.65s

Here the difference is brutal. ValueProviders are way superior to other approaches when it comes to browsers without native support to webcomponents. And in this case, with this size of Grid, Template and ComponentRenderers are just "too slow". The bottleneck is not in the extra communication needed for the components to work, but in the js engine itself and the amount of polyfills needed to make templates work reliably in the browser.

Conclusion

The simpler your columns are, the faster they are to render. Sounds like captain obvious, but it is what it is, the numbers speak for themselves. If you need to support IE11 with your app, either use small Grids, or use ValueProviders to show the data to your users. If you don't need to support IE11, then TemplateRenderers are an option for more complex structures, while ComponentRenderers should be used only for specific columns that are too hard to represent with pure HTML from the server side.

Gilberto Torrezan Filho
Gilberto Torrezan Filho
Gilberto started in Vaadin as a Java developer in the Vaadin Flow team, back in 2017. Currently he works as Product Owner of the vaadin.com website.
Other posts by Gilberto Torrezan Filho