Let's benchmark the DOM

Hi 👋
I was recently asked to improve chatroom performance. The longer chats became, the more users complained that everything is sluggish.

The chatroom was built in Backbone and jQuery and I tried many ways to make it better. Everything was hard and cumbersome. In the end I realized that re-rendering the whole list of messages, even without a smart framework, is fast enough. That made me wonder

"Did the DOM get fast?" 🧐

Below are a few benchmarks. Click buttons to see your own results. Charts for what I saw. :)

  • create a long list
  • append to it
  • prepend to it
  • insert in the middle
  • remove elements

Benchmarks focus on long flat lists of nodes because that's pretty common. Think chat window with thousands of messages. Our goal is to find which is faster

  • Raw DOM with vanilla JS
  • React
  • Vue
  • Preact

Don't worry, benchmarks are implemented in the respective framework internally. I'm just using React for the skeleton because it's what I'm used to and nwb made it quick to set up compiling and stuff. You can see the code on GitHub.


React

Implemented as a single component, no state management lib. Time measured time is between componentWillUpdate and componentDidUpdate

Time to render: 2ms
Current count: 0
Average time: NaNms

My results

Each test repeated 3 times on a prod build of React.

Avg of Avg time to prepend 1000 nodes up to 30,000: 21ms
Avg of Avg time to insert 1000 nodes up to 30,000: 18ms
Avg of Avg time to append 1000 nodes up to 30,000: 15ms
Avg time to drop 30,000 nodes: 195ms
Avg time to remove 1 node from 30,000: 27ms

Naive Vanilla JS

Implemented as a React component for buttons and onClick event handlers. Relevant DOM manipulation built with vanilla JavaScript naively recreating the DOM each time.

Time to render:
Current count:
Average time:

Smart Vanilla JS

Implemented as a React component for buttons and onClick event handlers. Relevant DOM manipulation built with vanilla JavaScript attempting to minimize amount of DOM changes.

Time to render:
Current count:
Average time:

Preact

Implemented as a single component, no state management lib. A Preact component is wrapped in a React component to fit into the project and handles its own rendering internally. Time measured is between componentWillUpdate and componentDidUpdate inside the Preact part.

Time to render: 9ms
Current count: 0
Average time: NaNms

Vue

Implemented as a single component, no Vuex. A Vue component is wrapped in a React component to fit into the project and handles its own rendering internally. Time measured is between beforeUpdate and updated inside the Vue part.

Time to render: 0ms
Current count: 0
Average time: 4ms