In this post we will explore three technologies to build dynamic DOMs. We will also run benchmarks and find out which one is faster. At the end we will share with you why we choose one of them for our projects. Read on!
There are many Document Object Model (DOM) manipulation frameworks and libraries. Among these, three have been the center of the attention due to their focus on performance: React.js, Ember.js and, more recently, Incremental DOM. While React and Ember handle much more than just DOM building/update, Incremental DOM focuses only on building DOM trees and allowing dynamic updates. We will now explore these libraries and find out which one is faster.
Before getting into the details, if you are not a web developer, you might be asking yourself what exactly DOM manipulation is. Web sites are built as trees of different elements. These elements are defined in the HTML spec. By composing these elements a developer can create arbitrarily complex web sites. The DOM is the abstract representation of a website as a tree of HTML elements. It is defined by a W3C spec and implemented by all major browsers.
Besides aiding in binding the data model to the view, these libraries help in doing updates to DOM efficiently. A series of updates that would normally be performed by manually issuing a series of DOM API calls can be automatically batched into a single call (or a reduced set of calls). For instance, suppose the logic behind an update to the site requires that you:
- Remove an element
- Add a new element
- Change a property of the added element
Directly issuing DOM API calls for doing changes such as these will result in intermediate repaints and reflows of the content. These are expensive operations. By working on a virtual model these steps can be flattened into one.
Templates are a popular way of building DOM trees. With templates a developer can use a specific template syntax that tells a template compiler how to turn that into a DOM tree (or HTML document). Templates can look like an extension of HTML, or be completely different.
Incremental DOM does not favor any particular template engine. However, being a Google sponsored project, a Closure templates backend is being developed. Incremental DOM can also be used with superviews.js, starplate and even JSX.
React.js' Virtual DOM
Disclaimer: some of the graphics in this post are based on the ones found in this excellent post explaining DOM manipulation libraries
- Fast and efficient "diffing" algorithm
- Multiple frontends (JSX, hyperscript)
- Lightweight enough to run on mobile devices
- Lots of traction and mindshare
- Can be used without React (i.e. as an independent engine)
- Full in-memory copy of the DOM (higher memory use)
- No differentiation between static and dynamic elements *
* React has recently implemented functionality that detects constants and reduces the number of elements that need to be checked for updates.
Glimmer is the name for Ember.js' latest rendering engine. Glimmer is the result of Ember's developers trying to integrate the benefits of React's Virtual DOM engine into Ember while maintaining API compatibility. Do note that Glimmer is a full rewrite of Ember's rendering engine and in no way shares code with Virtual DOM.
Glimmer differentiates between static and dynamic components, thus reducing the number of elements that need to be checked when looking for changes. This differentiation can be achieved thanks to the expressiveness of Handlerbar's templates.
Another key difference between Glimmer and other solutions lies in the way nodes are stored and compared. Glimmer stores nodes as simple stream-like objects (that is, simple queues of values) rather than full-fledged DOM-like nodes. To find out whether a real DOM node needs updating, the final value of a Glimmer node is compared to the last known real DOM value. If the value has not changed, no further actions are taken.
- Fast and efficient diffing algorithm
- Differentiation between static and dynamic elements
- 100% compatible with Ember's API (you get the benefits without major updates to your existing code)
- Lightweight in-memory representation of the DOM
- Meant to be used only in Ember
- Only one frontend available
Incremental DOM tries to bring a simpler approach to the table than the alternatives: rather than keeping a full in-memory representation of the DOM, or keeping a tree of lightweight elements, Incremental DOM uses the real DOM to find differences when data changes. You might be asking yourself why, if this is simpler, it hasn't been the solution picked by other libraries all along. Simple: it results in a tradeoff between speed and memory. Incremental DOM, by removing the additional copy of the DOM, results in reduced memory usage. In practice this also results in reduced speed while looking for differences. The reduced memory usage is key for mobile or other memory constrained devices. Read our article on Incremental DOM for more information on this.
- Reduced memory usage
- Simple API
- Easily integrates with many frontends and frameworks (meant as a template engine backend from the beginning)
- Not as fast as other libraries (this is arguable, see the benchmarks below)
- Less mindshare and community use
For our benchmarks we have picked the dbmonster test app as shown in this post.
dbmonster is a simple application that simulates the load caused by an application updating tons of rows in a table that shows the activity of different simulated database clusters. This application was originally developed to test Ember's performance. We have run this test using the latest versions of React, Ember 1.x and 2.x (both use Glimmer) and Incremental DOM. All tests were run using Chromium 46 on Linux (Core i5-5200U CPU). Five passes were performed for each test and then averaged.
In these charts the time spent during major and minor GC collections can be seen. As expected, Incremental DOM is much more efficient in this area. React remains a close second for major collections but falls way behind Incremental DOM in minor collections. It is interesting to note how much Ember has progressed from version 1 to version 2 in this area.
Ember shines in this case: time spent doing layout operations and then performing the actual paint to the screen. Incremental DOM trades memory usage for speed so it is expected in this case to see it behind the alternatives. React stays close to Ember and appears balanced so far.
This charts shows the number of frames that Chrome decided to drop (i.e. to stop drawing) due to long pauses. Dropped frames result in lower framerates and visible pauses. In this case, Incremental DOM shines again. Less time spent in GC pauses means more time is available to draw frames. React, Ember 1 and Ember 2 all remain close behind Incremental DOM.
Get the full code for the tests. To run the tests, you will need ChromeDriver and all node.js dependencies for the test driver (json2csv, browser-perf). Once all dependencies are installed, run
node run-benchmarks.js. Once the tests are done, results can be found in
data.json (full) and
Aside: React.js use at Auth0
At Auth0 we are constantly evaluating the best technologies for our platform. For the development of our Passwordless Lock library we picked React.js. React is an excellent choice because of its unopinionated integration model and its declarative way of developing components. On other fronts, React remains a well balanced approach between speed, memory usage, ease of integration and good documentation/support.
Virtual DOM, Glimmer and Incremental DOM are all excellent options for handling dynamic DOM updates. React's mindshare and ease of integration make it a no brainer for many projects. Increased memory use can be a problem in memory constrained devices for big websites. This problem, however, is getting smaller everyday as mobile devices carry more and more memory. Incremental DOM surprises by remaining fast even when doing less. We look forward to seeing Incremental DOM integrated into Closure and other libraries. React and Ember remain as well balanced approaches each favoring different development methodologies.
"Virtual DOM, Glimmer and Incremental DOM are all excellent options for handling dynamic DOM updates."
When picking one of these libraries focus on mindshare and ease of integration unless you are pushing the number of updates per frame to the limit. In that case, study carefully the results from this post and don't forget to run your own specialized benchmarks.