The DOM (Document Object Model) is an interface that represents an HTML document in a tree-like structure with nodes. This structure allows the document to be traversed and modified by programmers with each node being represented as an object. The DOM is created by the browser when
a web page is loaded.
"The browser parses HTML into a DOM tree. HTML parsing involves tokenization and tree construction. HTML tokens include start and end tags, as well as attribute names and values. If the document is well-formed, parsing it is straightforward and faster. The parser parses tokenized input into the document, building up the document tree."
Every time there is a change in UI, the DOM updates and the UI must be re-painted. This re-painting or re-rendering is a slow process because CSS must be re-calculated, the geometry of the layout must be computed, and the browser must paint the elements on the screen. This is where React's version of the DOM comes in.
Once the VDOM is created, React compares this new representation with a snapshot of the previous version of the virtual DOM to see exactly which elements have changed.
Once the difference is known, React updates only those objects that differ on the actual DOM and the browser re-paints the screen. The next time state or props changes for a component in the application, a new virtual DOM tree of React elements will be created and the process will repeat.
The process of checking the difference between the new VDOM tree and the old VDOM tree is called "diffing". Diffing is accomplished by a heuristic O(n) algorithm. During this process, React will deduce the minimum number of steps needed to update the real DOM, eliminating unnecessary costly changes. This process is also referred to as reconciliation.
From the React Docs:
"Instead, React implements a heuristic O(n) algorithm based on two assumptions:
- Two elements of different types will produce different trees.
- The developer can hint at which child elements may be stable across different renders with a key prop."
As a side note, when an element contains multiple child nodes, keys can be useful for React to keep track of which children change. The diffing algorithm will check for differences in child elements step by step, so if a child element is added at the end, that addition will be noted as the only update. However, for example, if an element was added at the beginning, then React may think that the following children also need to be updated because there will be difference in elements line by line. As an example from the docs:
<li>Duke</li> <li>Villanova</li> </ul> <ul> <li>Connecticut</li> <li>Duke</li> <li>Villanova</li> </ul>
Without the use of keys, "React will mutate every child instead of realizing it can keep the
<li>Villanova</li> subtrees intact. This inefficiency can be a problem."
By using the key attribute, React will be able to tell which children already existed or need to be updated and which don't.
Another thing that makes React's "virtual DOM" faster, is that it can batch updates to the DOM. React will wait for a batch of updates to be ready and will execute all the updates to the DOM in one step so that re-painting will only be done once.
To recap, manipulation of the DOM is not inherently slow, however re-painting of the UI is costly. React's "virtual DOM" helps to minimize these painting events by making sure only the elements that need to be changed in the DOM are manipulated, and that these updates are sent in batches. These batches of updates prevent unnecessary "slow" painting events and make React applications more efficient.
Cover photo by Min An from Pexels