Skip to content
Swizec Teller - a geek with a hatswizec.com

Building an interactive DOM benchmark, preliminary results

This is a Livecoding Recap – an almost-weekly post about interesting things discovered while livecoding. Usually shorter than 500 words. Often with pictures. Livecoding happens almost every Sunday at 2pm PDT on multiple channels. You should subscribe to My Youtube channel to catch me live.

After that chatroom windowing feature I built a few weeks ago, I've been thinking a lot about how fast native DOM manipulation has gotten.

I've been listening to the "DOM is slow" mantra my whole life. But with that feature, I discovered that the DOM is pretty fast, actually.

Did you know it's faster and easier to throw out your DOM nodes and render from scratch? That surprised me.

So I started working on this DOM manipulation benchmark. My goal is to give you a live benchmark that you can play with. A way to feel how fast or slow different approaches are.

It's not complete yet. You can already test React using local state, a naive vanilla JS implementation, and a smart vanilla JS implementation. I'm adding Vue, Preact, Angular, and maybe some others.

The test focuses on long flat lists. Those are the most common source of having thousands of nodes. Think chatrooms, news feeds, comments, data viz. All long flat lists of nodes.

We're testing how fast it is to

  1. Prepend 1000 nodes
  2. Insert 1000 nodes in the middle
  3. Append 1000 nodes
  4. Drop all nodes
  5. Remove 1 random node

React using local state

React is fast. Between 15ms and 21ms on average to prepend, insert, and append 1000 nodes. Removing 30,000 nodes is slower because it has to change a lot of individual nodes.

The benchmark benefits from nodes being stable. That means a node with the same key never changes. You can see the code on GitHub.

We manipulate a list in this.state and measure times between componentWillUpdate and componentDidUpdate. This should exclude the time it takes to manipulate our list.

What surprised me is the stark difference between dev-mode React and production-built React. React in dev mode is not only slow, but also gets increasingly slower the more nodes you're rendering. In production mode performance is constant.

Neat. πŸ‘Œ

Naive vanilla JS

The naive approach is slow. Faster than you'd expect, good for small lists, but not a scalable solution. The more nodes you render, the slower it gets.

The core of this benchmark is the render code. Drops all nodes and renders from scratch every time.

naiveRender() {
let start = new Date();
// remove all existing nodes
// from https://stackoverflow.com/questions/3955229/remove-all-child-elements-of-a-dom-node-in-javascript
let scratchpad = this.refs.scratchpad;
while (scratchpad.firstChild) {
scratchpad.removeChild(scratchpad.firstChild);
}
// append all nodes from scratch
this.nodes.forEach(k => {
let node = document.createElement("div");
node.appendChild(document.createTextNode(k));
scratchpad.appendChild(node);
});
let end = new Date();
this.times.push(end - start);
// update meta info
this.refs.time.innerHTML = `<code>${end - start}ms</code>`;
this.refs.currentCount.innerHTML = this.nodes.length;
this.refs.avgTime.innerHTML = this.averageTime;
}

You can see the full benchmark on GitHub.

This renders 1000 nodes in about 4ms, which is faster than React. But 4000 takes 32ms. Almost three times as much as React.

Curiously similar performance curve to dev-mode React. πŸ€”

Smart vanilla JS

A slightly smart vanilla JS approach is blazing fast. Constant performance around 2ms. Wat 🀨

I used the new prepend() and append() DOM methods. Nothing super clever, just a basic implementation of what we're testing.

You can see the whole benchmark on GitHub, but here's the prepend code for example.

prepend = () => {
let nodes = this.newNodes,
scratchpad = this.refs.scratchpad;
this.nodes = [...nodes, ...this.nodes];
let start = new Date();
nodes.map((k) => {
let node = document.createElement("div");
node.appendChild(document.createTextNode(k));
return node;
});
scratchpad.prepend(nodes);
this.updateMeta(start);
};

We're still manipulating the this.nodes array. Keeps "data" and DOM in sync, stays consistent to the other implementations.

Then we walk through the list of new nodes, create div elements for each of them, attach some text, and finally prepend() the whole array of nodes into the DOM. This, it turns out, is fast. πŸ”₯

1000 nodes in 1ms to 2ms. A tenth of the time it takes React to do it. Similar results for inserting and appending:

  • appends in about 2ms
  • inserts in about 5ms

Inserting is slowest because there's no magic method for it. You have to insertBefore each DOM node individually. The browser then has to shove the entire list around to make room.

Even dropping all nodes is crazy fast despite dropping 1-by-1 πŸ‘‰ around 2ms for 40,000 nodes. Wow.

Conclusion

In conclusion, vanilla JavaScript is fast if you know what you're doing. Faster probably than anything else I can add to this benchmark.

But it's not as powerful as React or a similar framework. It takes more time to build, it's hyper optimized for this example, and it wouldn't scale in a real world environment.

Code is hard to maintain. You waste time thinking about rendering instead of what you're building, and you will cry as soon as nodes stop being stable. Just imagine figuring out by hand which nodes' contents changed and which didn't.

That's what React is doing for you πŸ‘‰ Spending a little runtime to save a lot of dev time.

Can't wait to add Vue and Preact to the mix.

Did you enjoy this article?

Published on February 27th, 2018 in Front End, JavaScript, Livecoding, Technical

Learned something new?
Want to become a high value JavaScript expert?

Here's how it works πŸ‘‡

Leave your email and I'll send you an Interactive Modern JavaScript Cheatsheet πŸ“–right away. After that you'll get thoughtfully written emails every week about React, JavaScript, and your career. Lessons learned over my 20 years in the industry working with companies ranging from tiny startups to Fortune5 behemoths.

Start with an interactive cheatsheet πŸ“–

Then get thoughtful letters πŸ’Œ on mindsets, tactics, and technical skills for your career.

"Man, love your simple writing! Yours is the only email I open from marketers and only blog that I give a fuck to read & scroll till the end. And wow always take away lessons with me. Inspiring! And very relatable. πŸ‘Œ"

~ Ashish Kumar

Join over 10,000 engineers just like you already improving their JS careers with my letters, workshops, courses, and talks. ✌️

Have a burning question that you think I can answer?Β I don't have all of the answers, but I have some! Hit me up on twitter or book a 30min ama for in-depth help.

Ready to Stop copy pasting D3 examples and create data visualizations of your own? Β Learn how to build scalable dataviz components your whole team can understand with React for Data Visualization

Curious about Serverless and the modern backend? Check out Serverless Handbook, modern backend for the frontend engineer.

Ready to learn how it all fits together and build a modern webapp from scratch? Learn how to launch a webapp and make your first πŸ’° on the side with ServerlessReact.Dev

Want to brush up on your modern JavaScript syntax?Β Check out my interactive cheatsheet: es6cheatsheet.com

By the way, just in case no one has told you it yet today: I love and appreciate you for who you are ❀️