There’s some confusion and misunderstanding over what benefits the use of a Virtual DOM in React.js provides.
We’ve all heard that updating the Document Object Model (DOM) directly is inefficient and slow. However, few of us actually have the data to back it up. The buzz about React’s Virtual DOM is that it is a more efficient way to update the view in a web application, but very few of us know exactly why and whether this efficiency will result in faster page render times.
Putting aside the other benefits of using React, such as one-way data binding and components, I will discuss what exactly the Virtual DOM is, and whether it alone justifies the use of React over other UI libraries (or no UI library at all).
Why do we need a UI Library?
Two of the most important ideas of reactive programming are that systems should be event-driven and responsive to state changes.
The DOM’s user interface components have internal state, and updating the browser isn’t as simple as just regenerating the DOM whenever something changes. If Gmail did this, for example, you would be constantly annoyed when the entire browser window refreshed in order to display a new message, wiping out the email you were composing.
The statefulness of the DOM is why we need user interface libraries and solutions such as key/value observation (which is used by Ember among others) or dirty checking (which is used by Angular). UI libraries handle watching for changes to the data model and updating the correct part of the DOM when these changes occur, or watching for changes in the DOM and updating the data model when they occur.
This type of watching and updating is called two-way binding, and it can often make working with user interfaces very complex and confusing.
What makes React different?
React uses one-way data binding to make things simpler. Every time you type in an input field in a React UI, for example, it doesn’t directly change the state of that component. Instead, it updates the data model, which causes the UI to be updated and the text you typed into the field appears in the field.
Is the DOM slow?
What is slow, however, is the layout that browsers have to do whenever the DOM changes. Every time the DOM changes, browser need to recalculate the CSS, do layout, and repaint the web page. This is what takes time.
Browser makers are continually working to shorten the time it takes to repaint the screen. The biggest thing that can be done is to minimize and batch the DOM changes that make redraws necessary.
This strategy of reducing and batching DOM changes, taken to another level of abstraction, is the idea behind React’s Virtual DOM.
How Does Virtual DOM Work?
Like the actual DOM, the Virtual DOM is a node tree that lists elements and their attributes and content as objects and properties. React’s render() method creates a node tree from React components and updates this tree in response to mutations in the data model, caused by actions.
Each time the underlying data changes in a React app, a new Virtual DOM representation of the user interface is created
This is where things get interesting. Updating the browser’s DOM is a three-step process in React.
- Whenever anything may have changed, the entire UI will be re-rendered in a Virtual DOM representation.
- The difference between the previous Virtual DOM representation and the new one will be calculated.
- The real DOM will be updated with what has actually changed. This is very much like applying a patch.
Is the Virtual DOM Slow?
One would think that re-rendering the entire Virtual DOM every time there’s a possibility that something has changed would be wasteful — not to mention the fact that at any one time, React is keeping two Virtual DOM trees in memory.
But, the truth is that rendering the Virtual DOM will always be faster than rendering a UI in the actual browser DOM. It doesn’t matter what browser you’re using: this is just a fact. But it’s also sort of irrelevant.
The problem is that your users can’t see the Virtual DOM. It’s like owning 10,000 tacos in another county. Sooner or later, you need have the tacos delivered, and that can be expensive and slow.
Sticking with the taco analogy (because, why not?), is it faster to have all of the tacos shipped to you at once, or to calculate the difference between how many tacos you need and how many tacos you own and just ship bare minimum number of tacos? It’s certainly less expensive to ship 4 tacos when you only want 4 tacos.
The next question is: how do you go about ordering the tacos? Do you say, “Send me 4 tacos” or do you say, “Here’s how I’d like my taco situation to look. You work out the details.”
The 2nd approach is how the Virtual DOM works. You write the code to make your UI look how you want it to look, and the Virtual DOM works out the difference between that and how it looks now and only updates what needs to be updated.
React does this magic by attaching attributes to elements in your document and manipulating them individually (using these very specific ID attributes) after doing the diff to determine what needs updating. The Virtual DOM inserts additional steps into the process, but it creates an elegant way to do minimal updates to the browser window without you having to worry about the actual methods being used or even what needs to be updated and when.
Let’s See Some Numbers!
I’m not going to do benchmarking tests. A number of other people have created different tests to figure out whether React’s Virtual DOM approach is faster. The most frequent conclusion seems to be that it isn’t, unless it is, but it doesn’t really matter because the tests aren’t realistic.
The Virtual DOM adds a layer of scripting on top of whatever optimizations the browser is already making in order to make these minimized DOM manipulations transparent to the developer. This additional layer of abstraction makes React much more CPU-intensive than other methods of updating the DOM.
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>Hello React!</title> <script src="build/react.js"></script> <script src="build/react-dom.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script> </head> <body> <div id="example"></div> <script type="text/babel"> ReactDOM.render( <h1>Hello, world!</h1>, document.getElementById('example') ); </script> </body> </html>
The native approach is always going to be faster. And just for kicks, let’s look at the proof.
Here’s the timeline for loading and rendering the direct DOM manipulation “Hello, World!” page (in Chrome).
And here’s the timeline for loading and displaying the React “Hello, world!” app in the same browser.
Notice that everything is essentially the same, except for time it takes to do the scripting. React is slower than using DOM methods directly, and by a lot! But, how does it compare to jQuery?
However, this is just common sense. Using a library is slower than not using a library. And creating an in-memory representation of the DOM before actually manipulating the DOM is slower than just manipulating the DOM directly, when it’s done correctly.
Now that we have the obvious out of the way, let’s discuss exactly how you can make using the Virtual DOM faster.
How to use the Virtual DOM
My “Hello, world!” examples are unfair to React because they only deal with the initial render of a page. React is designed for managing updates to pages.
Because of the Virtual DOM, each change to the data model can trigger a complete refresh of the virtual user interface. This is very different from systems used by other libraries, which observe aspects of the document and update them when necessary. The Virtual DOM often actually uses less memory than other systems, because it doesn’t need to hold observables in memory.
However, there are inefficiencies when you’re comparing two entire virtual DOMs each time an action occurs. The CPU requirements can be formidable for complex user interfaces.
For this reason, React developers can’t be completely passive when it comes to deciding what to render. If you know that a certain action won’t affect a certain component, you can tell React to not analyze that component looking for changes — saving significant resources and significantly speeding up your application. Demonstrations of React’s performance often include raw numbers showing high CPU usage followed by the dramatic improvements that can be realized with good development practices such as memoization.
The truth is, there may be no way to actually figure out if using Virtual DOM is faster than an approach that updates the DOM directly, because it depends on a million different factors, but mostly it depends on how well you optimize your application.
This isn’t surprising or revolutionary. Any tool is only as good as the person who’s using it. What React and the Virtual DOM give us, however, is a simpler way to think about updating the browser. This simplicity can free up significant mental resources and make optimizing the user interface easier. This is where the real benefits — both in performance and productivity — lie in React.
Author: Chris Minnick, one of Accelebrate’s instructors.