Why exactly do we need a Virtual DOM?
NOTE: Watch the Youtube video here : https://youtu.be/zEuO9cyc4RE
Before getting into Virtual Dom you should be clear with DOM (Document object model), and in order to understand it clearly, we’re going to understand the following and then jump to Virtual DOM.
- Browser Structure
- Rendering Engine
- Critical Rendering Path
- Why not just DOM?
Browser Structure
- User Interface: Everything that you interact with is the user interface component, the address bar, the bookmark button, the forward-backward button, etc.
- Browser Engine: This is the link between Rendering Engine and the UI component, it acts as a bridge for all actions between these two.
- Rendering Engine: This is the elephant in the room, our primary focus here. This component is solely responsible for parsing requested HTML, XML, etc., creating the layout, and finally painting it on the screen. We will come back to this in a moment.
- Networking: This is responsible for all those network requests that your browser sends through various protocols such as HTTP, FTP, etc. It is also a watchman for security-related issues with internet communication.
- Javascript Interpreter: Name says it all, this interprets the JS code. JS code is parsed and executed here and then it is transferred to the render engine. (Also JS is a compiled language or is it an interpreted language? Scratch your head )
- UI Backend: This is a platform-independent component that uses the operating system user interface, mainly used for drawing combo boxes and windows.
- Data Persistence: All those cookies, tokens that you store are handled by this persistence layer.
Now let’s bring our render engine back:
Let’s say you requested an index.html file in your browser, now a series of events will happen in the render engine, firstly the engine here will start fetching the contents for index.html, and here the networking layer will help. The engine will receive contents in chunks of 8KBs and once it is fetched the basic flow begins:
- The HTML is parsed and tokens are created, these tokens contain the information of HTML tags and have their own set of rules, these are then created into Objects which act as NODES in the DOM Tree, and hence DOM is created. Here the DOM is containing all the content on the page, what about the style then? Well, for style in a similar manner a CSS Object Model is created also called CSSOM and it contains nodes with all the styles for the page.
- Till now we have both styles and content, we just have to stitch them together. For this, we have another tree (yes one more:\ ) and for that, we move to the second step of the render engine.
- A render tree is created by combining DOM and CSSOM. To do this browser starts from the root of the DOM tree and for every node determines which rules are to be applied. Interestingly it only captures the visible content so if CSS is “display: none” for some HTML object it is ignored and therefore the head section of HTML (in most cases) is not included in the render tree. Now we have styles for each DOM node what next?
- Then the render engine calculates the exact position and size for each node of the render tree. Imagine your screen to be a graph and in the “Layout Process” the render engine assigns coordinates to the nodes. This ensures that every content is shown at an accurate position on the screen. Finally, once the size and position are calculated render engines move to the last step.
- To paint the render tree, the engine traverses the tree and invokes the rendering engine’s paint() method to paint every node on the screen and hence we get our document index.html on our screen.
So the path followed here by rendering engine in four steps is known as Critical Rendering Path, there are ways to increase the efficiency and speed of this path but this too has limitations.
Finally, before going to the Virtual DOM question is where DOM fails?
Finally, before going to the Virtual DOM question is where DOM fails?
Many developers say that DOM fails because updating DOM is slow and inefficient, well truth be spoken it is not the manipulation that takes time it is the rendering process that happens after every update which makes the DOM Manipulation slower. I just explained what happens in the render engine, you get my point, right?
But then even in React, we manipulate the DOM, we change the states and everything does that mean React is changing the rendering process? NO
The rendering process is still the same, React helps in reducing the number of times we change our DOM and hence reduces the number of times we have to render the document.
Let us say we have a list of 3 List Items in our document:
let items=["pikachu","squirtle","ratata"]
and we decided to change one of the items here:
items=["pikachu","squirtle","charizard"]
even for this one change, the whole list will be re-created and the document will update, one such operation is fine. Imagine thousands and millions of such manipulations in the DOM and for each manipulation rendering the UI by critical rendering path, this is what makes it slower to update the UI for the user.
Now let us introduce Virtual DOM:
According to React docs,
The virtual DOM (VDOM) is a programming concept where an ideal, or “virtual”, representation of a UI is kept in memory and synced with the “real” DOM by a library such as ReactDOM
Notice that the virtual DOM is never rendered by the browser it is always kept in the memory. The virtual DOM is like a blueprint of the actual DOM and helps in making React faster. How exactly does this work?
- In React we have React elements, remember React.createElement?
- When we render() our React component for the first time these React Elements of JSX are used to create DOM and simultaneously a virtual DOM is also created.
- Imaging we made certain changes and called the render() method again, now unlike DOM which blindly manipulates, React is smart it goes into reconciliation and the magic happens here.
- Right before updating the change in its virtual DOM React takes a snapshot of it and calls it previous Virtual DOM and updates the current Virtual DOM. At this point, we have two Virtual DOMs previous and current. React compares both of these using its “Diffing Algorithm” which picks only those nodes which are changed and then calculates the minimum number of steps required in order to make those changes in the actual DOM.
- In our above example only items[2] was changed so it picks that node, now imagine there are hundreds of such changes, and by comparing the two virtual DOMs React calculates the minimum number of steps to make all the changes and it then creates a batch.
- This batch of all the steps is executed by the event loop in one go and hence the actual DOM is patched only once. So here React reduced the number of rendering UI. Since an entire batch is updated to actual DOM in one go, if in between something else is changed it has to go in another batch once the event loop is free and this cycle continues.
There are some generic solutions to this algorithmic problem of generating the minimum number of operations to transform one tree into another. However, even the state-of-the-art algorithms have a complexity in the order of O(n3) where n is the number of elements in the tree.
If we used this in React, displaying 1000 elements would require in the order of one billion comparisons. This is far too expensive. 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. - You must have seen those key attribute warnings in the console while creating React Apps, now you know why :)
This was all about VIRTUAL DOM and why we use this in React and other such SPAs.