JS web frameworks benchmark – keyed vs. non-keyed

The next round of the js-framework-benchmarks will make a distinction between “keyed” and “non-keyed” implementations. This blog post explains what it means and why this distinction is relevant.

All modern frameworks have some kind of binding between data and DOM nodes. This binding is especially interesting when data consists not of a single item, but of a list of items. If data changes the question is which DOM nodes should be updated, inserted or removed.
The answer to this question has an impact on performance, usability of 3rd-party javascript libraries and css transitions.
Some frameworks like react, vue.js or angular allow to create a 1:1-relationship between a data item and a DOM node by assigning a “key” attribute (or for angular specifying “trackBy” in *ngFor). If you use some identifier of the data as the key you get the “keyed” mode. Any update to the data will update the associated DOM node. If you reorder the list, the DOM nodes will be reordered accordingly.

The other mode is “non-keyed” and this is what e.g. vue.js uses by default for lists. In this mode a change to the data items can modify DOM nodes that were associated with other data before. This can be more performant, since costly DOM operations can be avoided (e.g. first removing old nodes, and the adding new nodes) and the existing DOM nodes are updated to display the new data. For react and angular using the item index as the key uses “non-keyed” mode for those frameworks.

Depending on your requirements the “non-keyed” mode can be a performance gain or can cause severe problems so one must choose carefully the mode and check that the framework supports that mode.

Let’s look at the difference in an example. In the js-framework-benchmark the replace all rows benchmark first creates a table of 1,000 rows and then repeatedly replaces those rows with 1,000 new rows. To see the effect let’s add an external effect: When clicking “Create 1,000 rows” the background color of the first row is randomly set, then we’ll delay replacing all rows for one second so we can see the background color before all rows are replaced.

In the “keyed” mode this means that first all rows are removed from the DOM and 1,000 new table rows are created and appended to the table. The background color is then removed:

The “non-keyed” mode simply updates the text of all existing rows which is much faster. The background color was set without the knowledge of the vdom-framework and stays visible:

If this actually matters is up to the requirements or your application. As a rule of thumb if all dom updates are managed by the framework and no CSS transitions are applied “non-keyed” might be a nice performance optimization. “keyed” should never cause any problems (except performance).

Another example is the remove row benchmark. In “keyed” mode the row on which the user clicked is removed from the dom. In “non-keyed” mode the contents of all rows below the one that should be deleted are shifted one row up and then finally the last row is deleted. While that sounds like much more work it can indeed be faster since the DOM style recalculation is much faster.

To make the effect visible we’ll add a CSS transition on the delete button. This is what “keyed” mode looks like:

And this is “non-keyed”

With that transition “non-keyed” is most likely not what the designer wanted.

If we compare the performance of keyed vs. non-keyed we can see quite a difference for the following benchmarks:

Since the DOM operations can’t be compared between both modes the result table of “keyed” and “non-keyed” benchmarks will be separated for future benchmarks rounds.

Further readings:

One thought on “JS web frameworks benchmark – keyed vs. non-keyed

Comments are closed.