What is virtual DOM
The react doc states that Virtual DOM
can be described as a technique rather than a technology, it allows us to update the DOM more efficiently. Most of the Frontend frameworks like React and Angular uses this techniuqe for faster rendering, so how does it work ?
Under the hood
Let’s briefly talks about how react uses this technique. React actually maintains a large object which simulates the DOM object internally which is the virtual DOM.
Whenever we calls setState (methods which trigger re-render). React will then compares the previous virtual DOM and the current virtual DOM(using what is so called a diffing algorithm) and figure out the a way to update the real DOM with minimal effort, this process is called reconciliation.
If react does not use virtual DOM, react will have to re-paint the whole DOM every time we called the setState method which not effective, with this techique, react could also batch updates to combine several setState into one for more efficient updating.
Above is an extremely simple introduction of how virtual DOM benefits react, actually react does way more than the content above. If you want to really understand how react does this, I am sure there are many fantastic articles out there.
I followed Jason Yu’s tutorial video for learning how to build a simple virtual DOM which really helps me understand it better, highly recommend.
Below I will start to talk into the details I found in this video.
The building blocks
These are the following building blocks we need to create a simple virtual DOM
createElement
This function helps us to create the virtual DOM object
render / renderElem
This function takes our virtual DOM object as a parameter and renders the actual DOM
diff
This function helps us check the differences between two virtual DOM objects and returns a method to update our actual DOM
mount
insert / replace a node to the actual DOM
These are the three main methods we need, on the way of creating the virtual DOM we will create additional helper methods.
Let’s build a simple virtual DOM
Step1. Creating a simple virtual DOM object
As we have mentioned, we need an object to simulate the actual DOM for making the minimal change, we will now create the object here.
As we know a HTML element node contains merely three parts
- Tag name
- Attributes
- Children- can be a textNode or another elementNode
First, we will create a function which returns an object with these three properties.
The function might be called as follow to create a virtual DOM. In the children we specify a textNode and an elementNode
Step2. Creating the render method to transform virtual DOM to actual DOM
Next we will create a render method to transform our virtual DOM object into the actual DOM.
As you can see the render method will deal textNode and elementNode in a different way.
renderElem is a function specifically deal with elementNode, it will do the following three things
- use document.createElement function to create an actual element
- loop through the attrs property to set the attribute of the element
- use recursion to check all the child node in the children array in same way with all of the nodes.
Now we got our actual node element, we have to insert into the DOM. In React we have an entry element <div id="app"></div>
, we can use the below mount function to replace the above tag with the actual DOM element we created.
Step3. Diffing two virtual DOMs for efficient update
Now in order to make the minimal change to our actual DOM, whenever the users updates the DOM, we need to compare the incoming virtual DOM and the current virtual DOM.
Here’s what we gonna do, we will create a diff function which takes in the current virtual DOM and the incoming virtual DOM as parameters and compare them.
This function returns a function which takes in a node, do some manipulations to it and returns it, baiscally looks like below
However there are four cases we need to deal with when comparing two virtual DOM objects
- case 1 - The new VDOM is undefined
- case 2 - When one of the VDOMs is a textNode
- case 3 - When tag names are different between each two VDOMs
- case 4 - When the tag names are the same
Case 1
When the newVDOM is empty, The diff function simply returns a function which removes this node
Case 2
When one of the node is a textNode , we simply compare to see if they are the same, if not we will replace the old node completely with the new node
Case 3
If the tag name between two nodes are different, simply replace it.
Case 4
The above cases are simple, but case 4 is the case we want to focus here, when the tag name of two nodes are the same how do we make the minimal change ?
Since our VDOM object has properties of attributes
and children
we will create a diffAttrs
and adiffChildren
function, pass in the corresponding properties, returns a function which allows us to make the minimal change.
Step4. Diffing the attributes and children
diffAttrs
differentiating the attributes is relatively easy compared to children. The attributes of each elementNode in the virtual DOM represents as a key value pair and here are what we are going to do.
- use the
setAttribute
function to set all the new attributes of the new VDOM node - use the
removeAttribute
function to check the attribute to be removed is not in the newVDOM node and remove old attributes
diffChildren
diffChildren is the most complicated part I think so far, here are the steps of what we are going to do, Let’s assume our old VDOM have children of 4 nodes and our new VDOM have children of 6 nodes
- Compare the min(oldVDOM.children, newVDOM.children) nodes
- if the new VDOM has more children nodes we will simply append the rendered children nodes.
The ultimate goal here is to handle all of the children in our new VDOM correctly.
First, take the above example we need to compare the first 4 nodes of the old one and the new one. we will create a zip function here. The zip function takes children of the two VDOMs and returns an array of [oldChildNode1, newChildNode2]
Next we will loop through this returned array and put the children nodes in to the diff
function we created earlier
Now we first get the corresponding handle functions for each of the childNode which will make only the minimal change to it, next, we will check if there are more childNodes on the new VDOM we have to append them.
The whole diffChildren
looks like below and here are the things we do
- Collect the corresponding diffing functions for existing childNodes
- If there are any new children, append them into the node.
- We zip our childPatches and new VDOM’s children and loop through it to apply the corresponding handling method to the existing children
- We loop through our additionalPatches array and append the new children to our VDOM
I know it’s a bit complicated I suggest that you watched Jason Yu’s video to get more insight if it is not clear enough
Step 5. See the result
We will simulate re-rendering the DOM every second using the setInterval method
We can see that we did not replace all the DOM tree every time we re-render, we are just performing the minimal change to our DOM
Conclusion
Big Appreciate to Jason’s video for letting me understand more about the internals of the virtual DOM. Though nowadays, we do not have to implement the virtual DOM ourselves, I think it’s good to know about how these frameworks do re-rendering efficiently.
Of course the real world ain’t that kinda easy, React uses a thing called fiber
as a new reconciliation engine and I think it is good to understand the virtual DOM and how React deals with the renders before stepping into it.