Why is React's concept of Virtual DOM said to be more performant than dirty model checking?
Asked Answered
Q

6

395

I saw a React dev talk at (Pete Hunt: React: Rethinking best practices -- JSConf EU 2013) and the speaker mentioned that dirty-checking of the model can be slow. But isn't calculating the diff between virtual DOMs actually even less performant since the virtual DOM, in most of the cases, should be bigger than model?

I really like the potential power of the Virtual DOM (especially server-side rendering) but I would like to know all the pros and cons.

Quadriceps answered 14/1, 2014 at 8:57 Comment(1)
I think you could mention this talk too youtube.com/watch?v=-DX3vJiqxm4 where he specifically talks about benchmarks.Aerodontia
G
511

I'm the primary author of a virtual-dom module, so I might be able to answer your questions. There are in fact 2 problems that need to be solved here

  1. When do I re-render? Answer: When I observe that the data is dirty.
  2. How do I re-render efficiently? Answer: Using a virtual DOM to generate a real DOM patch

In React, each of your components have a state. This state is like an observable you might find in knockout or other MVVM style libraries. Essentially, React knows when to re-render the scene because it is able to observe when this data changes. Dirty checking is slower than observables because you must poll the data at a regular interval and check all of the values in the data structure recursively. By comparison, setting a value on the state will signal to a listener that some state has changed, so React can simply listen for change events on the state and queue up re-rendering.

The virtual DOM is used for efficient re-rendering of the DOM. This isn't really related to dirty checking your data. You could re-render using a virtual DOM with or without dirty checking. You're right in that there is some overhead in computing the diff between two virtual trees, but the virtual DOM diff is about understanding what needs updating in the DOM and not whether or not your data has changed. In fact, the diff algorithm is a dirty checker itself but it is used to see if the DOM is dirty instead.

We aim to re-render the virtual tree only when the state changes. So using an observable to check if the state has changed is an efficient way to prevent unnecessary re-renders, which would cause lots of unnecessary tree diffs. If nothing has changed, we do nothing.

A virtual DOM is nice because it lets us write our code as if we were re-rendering the entire scene. Behind the scenes we want to compute a patch operation that updates the DOM to look how we expect. So while the virtual DOM diff/patch algorithm is probably not the optimal solution, it gives us a very nice way to express our applications. We just declare exactly what we want and React/virtual-dom will work out how to make your scene look like this. We don't have to do manual DOM manipulation or get confused about previous DOM state. We don't have to re-render the entire scene either, which could be much less efficient than patching it.

Grazia answered 2/6, 2014 at 13:37 Comment(11)
Does React do dirty checking on component props? I ask because there's no setProps() function.Bicollateral
There's a setProps: facebook.github.io/react/docs/component-api.html#setpropsTeledu
React only re-renders implicitly when you use 'setState'. If you just change the props, you have to re-render manually, using renderComponent.Loathly
@AlexG: I don't know how this was before, but setProps() definitely triggers a re-render of the component if the properties have changed. Not sure, but I would hazard React components only do shallow comparisons of their props, so don't go messing with props you have already set (use React.addons.update instead :-) )Augmenter
So in React, are there three DOMs? The real DOM, a second Virtual DOM that is a reflection of the real DOM, and a third DOM which is the one that gets dirty and which is diffed against the second to determine what has changed? If so, where is the second DOM stored? Is it stored internally by the React library and always reflects the real DOM? I'm wondering because I'd like to know if replacing the root-most React component in a website causes the second DOM (and hence the real DOM) to be replaced.Meatus
If there are three DOMs, what happens when someone modifies the DOM with jQuery, outside of React's control, and the three DOMs are all different?Meatus
Or, is there the real DOM, and for each root React component that is rendered into some location using React.render, there are two virtual DOMs? So, for example, if I call React.render to put two React components into two different places, does that mean there will be a total of 5 DOMs (the real one, two virtual DOMs for the first component, and two virtual DOMs for the second component)?Meatus
what would be an example of such unnecessary re-renders ?Hopper
When you say "So while the virtual DOM diff/patch algorithm is probably not the optimal solution", do you have in mind a theoretically more optimal solution?Ilyse
This doesn't quite seem to answer the question. React requires you to use setState to signal that the state has changed. If you were able to do this.state.cats = 99 you would still need dirty checking to check for the model change, just as Angular dirty checks the $scope tree. This isn't a comparison of the speed of the two techniques, it's simply a statement that React doesn't do dirty checking because it has a Backbone style setter instead.Dobson
@Bicollateral - No, React doesn't dirty check props, it just re-renders the whole thing in JavaScript when setState is called on a parent (which is fast) then diffs the DOM. Don't use setProps, it's deprecated.Dobson
H
135

I recently read a detailed article about React's diff algorithm here: http://calendar.perfplanet.com/2013/diff/. From what I understand, what makes React fast is:

  • Batched DOM read/write operations.
  • Efficient update of sub-tree only.

Compared to dirty-check, the key differences IMO are:

  1. Model dirty-checking: React component is explicitly set as dirty whenever setState is called, so there's no comparison (of the data) needed here. For dirty-checking, the comparison (of the models) always happen each digest loop.

  2. DOM updating: DOM operations are very expensive because modifying the DOM will also apply and calculate CSS styles, layouts. The saved time from unnecessary DOM modification can be longer than the time spent diffing the virtual DOM.

The second point is even more important for non-trivial models such as one with huge amount of fields or large list. One field change of complex model will result in only the operations needed for DOM elements involving that field, instead of the whole view/template.

Hasty answered 14/1, 2014 at 15:34 Comment(9)
Actually I have read some articles too, so I now (at least in general) how it works, I just wanted to figure out why it can be more efficient than dirty check of model. And 1) Yup, it does not compare models but does compare much bigger virtual dom 2) Dirty-check of model provide us with ability to update only what needed too (as Angular does)Quadriceps
I believe only parts of the virtual DOM corresponding to the changed component have to be compared, while dirty-checking happens every digest loop, for every values on every scopes, even if nothing changed. If large amount of data changed, then Virtual DOM would be less efficient, but not for small data change.Hasty
Speaking of Angular, because watchers can also mutate the state while digest, the $scope.$digest is executed multiple times per digest cycle, so it's multiple time of full data comparison versus single time of partial virtual DOM tree comparison.Hasty
it is sad so many smart developers invent "mountains" of tricks to deal with "slow" DOM and so on, instead of focusing our combined attention to just fix the browsers themselves and rid us of DOM slowness once and for all. it is like using all humanity's resources to research ways to deal with cancer and improve a patient's life, instead of just fix the cancer itself. Ridicules.Hopper
@Hopper The DOM needs to display stuff on the screen. A virtual DOM doesn't. Even with some ideal performing DOM, creating a virtual DOM will be faster.Hungarian
@Hungarian - I am highly sceptical of the perceived speed gain in comparison with traditional ways. is there any known study on this matter?Hopper
Also it sounds like a great idea not to waste energy trying to cure a disease that is incurable because its a basic side effect of how cells work. Just like it seems sensible not to.try and solve a problem that's a basic function of the design of CSS. See also: thinking harder at a problem doesn't make it go away.Pincince
Are you then modifying the DOM when React patches the DOM with the Virtual DOM? Doesn't the DOM then re-render/paint the whole page?Move
IMO - In My OpinionBrink
E
76

I really like the potential power of the Virtual DOM (especially server-side rendering) but I would like to know all the pros and cons.

-- OP

React is not the only DOM manipulation library. I encourage you to understand the alternatives by reading this article from Auth0 that includes detailed explanation and benchmarks. I'll highlight here their pros and cons, as you asked:

React.js' Virtual DOM

enter image description here

PROS

  • 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)

CONS

  • Full in-memory copy of the DOM (higher memory use)
  • No differentiation between static and dynamic elements

Ember.js' Glimmer

enter image description here

PROS

  • 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

CONS

  • Meant to be used only in Ember
  • Only one frontend available

Incremental DOM

enter image description here

PROS

  • Reduced memory usage
  • Simple API
  • Easily integrates with many frontends and frameworks (meant as a template engine backend from the beginning)

CONS

  • Not as fast as other libraries (this is arguable, see the benchmarks below)
  • Less mindshare and community use
Epsilon answered 3/5, 2016 at 19:34 Comment(1)
The representation of ReactJS's DOM manipulation seems little off to me. The ReactJS's virtual DOM is the one which changes entirely, not the actual DOM - correct? I am looking at the original article the referenced article references and here is what I see - teropa.info/images/onchange_vdom_change.svg. teropa.info/blog/2015/03/02/…Mitre
D
36

Here's a comment by React team member Sebastian Markbåge which sheds some light:

React does the diffing on the output (which is a known serializable format, DOM attributes). This means that the source data can be of any format. It can be immutable data structures and state inside of closures.

The Angular model doesn't preserve referential transparency and therefore is inherently mutable. You mutate the existing model to track changes. What if your data source is immutable data or a new data structure every time (such as a JSON response)?

Dirty checking and Object.observe does not work on closure scope state.

These two things are very limiting to functional patterns obviously.

Additionally, when your model complexity grows, it becomes increasingly expensive to do dirty tracking. However, if you only do diffing on the visual tree, like React, then it doesn't grow as much since the amount of data you're able to show on the screen at any given point is limited by UIs. Pete's link above covers more of the perf benefits.

https://news.ycombinator.com/item?id=6937668

Decato answered 14/1, 2014 at 17:13 Comment(2)
Actually about last paragraph: it should be wrong: model is bigger than virtual dom because for each model value there is (in most cases) at least one virtual dom element (and usually much more than one). Why do I want model that is not shown?Quadriceps
Paginating cached collections.Zone
L
0

In React, each of your components have a state. This state is like an observable you might find in knockout or other MVVM style libraries. Essentially, React knows when to re-render the scene because it is able to observe when this data changes. Dirty checking is slower than observables because you must poll the data at a regular interval and check all of the values in the data structure recursively. By comparison, setting a value on the state will signal to a listener that some state has changed, so React can simply listen for change events on the state and queue up re-rendering.The virtual DOM is used for efficient re-rendering of the DOM. This isn't really related to dirty checking your data. You could re-render using a virtual DOM with or without dirty checking. You're right in that there is some overhead in computing the diff between two virtual trees, but the virtual DOM diff is about understanding what needs updating in the DOM and not whether or not your data has changed. In fact, the diff algorithm is a dirty checker itself but it is used to see if the DOM is dirty instead.

We aim to re-render the virtual tree only when the state changes. So using an observable to check if the state has changed is an efficient way to prevent unnecessary re-renders, which would cause lots of unnecessary tree diffs. If nothing has changed, we do nothing.

Lazarus answered 14/5, 2022 at 9:35 Comment(0)
C
-2

Virtual Dom is not invented by react. It is part of HTML dom. It is lightweight and detached from the browser-specific implementation details.

We can think virtual DOM as React’s local and simplified copy of the HTML DOM. It allows React to do its computations within this abstract world and skip the “real” DOM operations, often slow and browser-specific. Actually there is no big differenc between DOM and VIRTUAL DOM.

Below are the points why Virtual Dom is used (source Virtual DOM in ReactJS):

When you do:

document.getElementById('elementId').innerHTML = "New Value" Following thing happens:
  1. Browser needs to parse the HTML
  2. It removes the child element of elementId
  3. Updates the DOM value with new value
  4. Re-calculate the css for the parent and child
  5. Update the layout i.e. each elements exact co-ordinates on the screen
  6. Traverse the render tree and paint it on the browser display

Recalculating the CSS and changed layouts uses complex algorithm and they effect the performance.

As well as updating the DOM properties ie. values. It follows a algorithm.

Now, suppose if you update DOM 10 times directly, then all the above steps will run one by one and updating DOM algorithms will take time to updates DOM values.

This, is why Real DOM is slower than virtual DOM.

Constringe answered 31/7, 2017 at 9:44 Comment(4)
About the example, if you are modifiying the dom directly or via a virtual dom, then finally for both cases you are changing the dom.Wares
Yes in both cases we are updating dom but in case of virtual dom it updates particular that key(uniquely defined by differs algorithm from react) field or element tag only. Whereas updating dom updates or refreshed the whole dom fully.Constringe
I have seen this article from hackernoon.com/virtual-dom-in-reactjs-43a3fdb1d130. Maybe it's better to point out the source if you are not the author.Very
"This, is why Real DOM is slower than virtual DOM." No sir, you are just wrong.Revolution

© 2022 - 2024 — McMap. All rights reserved.