This is the 21st day of my participation in the November Gwen Challenge. Check out the event details: The last Gwen Challenge 2021
Over the past decade, front-end technology has changed rapidly. From the earliest purely static pages, to the dominance of jQuery, to the popularity of the MVVM framework in recent years — the development mode of upgrading this thing for the front end, it seems to become a kind of normal. In fact, behind the continuous evolution of the research and development mode, it precisely contains the front-end people’s continuous thinking and improvement of the core action of “DOM operation”. And the virtual DOM is a pearl that the pioneers bred in this process.
Let’s start with two questions:
- What is the virtual DOM
- How does the virtual DOM solve the problem
- Virtual DOM, really for better performance?
In the MVVM framework subfield, there is a still classic interview question: “Why do we need the virtual DOM?” .
A common response to this question is: “DOM manipulation is slow, JS is fast, and direct manipulation of DOM can result in frequent backflow and redraw, which is not a problem with JS. So the virtual DOM is faster than the native DOM.” But is it really true?
Two “big problems” with the Virtual DOM
Virtual DOM (Virtual DOM) is essentially a mapping cache between JS and DOM, which is represented as a JS object that can describe the DOM structure and its attribute information. The transformation relationship between JSX and DOM was discussed before, and the form of virtual DOM in React was mentioned.
For the virtual DOM, there are two things to keep in mind:
- The virtual DOM is a JS object
- The virtual DOM is a description of the real DOM
This basically solves the question of what the virtual DOM “is.” Here’s a look at how the React virtual DOM works. The virtual DOM appears as a “key person” during the mount and update phases of the React component, and its workflow is as follows:
- In the mount phase, React will combine JSX description to build a virtual DOM tree, and then use reactdom.render to map the virtual DOM to the real DOM (triggering the rendering line).
- In the update stage, the changes of the page will be applied to the virtual DOM before they are applied to the real DOM. The virtual DOM will compare the specific real DOM that needs to be changed with the help of the algorithm in the JS layer, and then apply these changes to the real DOM.
There are many questions about the virtual DOM, such as: “Why do you need the virtual DOM?” “, “What are the advantages of the virtual DOM?” “, “Does the virtual DOM come with better performance?”
To answer the endless whys, don’t look at the question point by point. The virtual DOM is a relative newcomer to previous DOM manipulation solutions. In order to understand the rationality of the existence and development of a new thing, it must be discussed in a long enough and reasonable context.
History of DOM manipulation solutions
1. The “human DOM” period dominated by native JS
There is no “flourishing” stage in front-end work like today. The “display” attribute of front-end page is much stronger than its “interaction” attribute, which leads to the positioning of JS can only be “auxiliary”. At this stage, as a front-end developer, although we have nothing, But we were happy — simple business requirements meant we didn’t have to do too much or too complicated DOM manipulation, and native JS was enough.
2. Pilot stage of productivity liberation: jQuery period
With the emphasis on the front end and the pursuit of a richer user experience, the front-end development workload has increased dramatically due to the need for DOM manipulation. In order to achieve efficient development, jQuery first solves the problem of “bad API” — it encapsulates the DOM API into a relatively simple and elegant form, while simultaneously doing away with cross-browser compatibility. It also provides a series of capabilities such as chain API calls and plug-in extensions to further liberate productivity. The end result is the much-loved “write less, do more”.
JQuery makes DOM manipulation simple, fast, and always stable in form and availability.
3. The beginning of people’s Wisdom: early template engine scheme
JQuery makes it easier to manipulate the DOM in a more comfortable position, but it doesn’t fundamentally solve the front-end stress associated with excessive DOM manipulation. The template engine solution, however, holds the promise of “liberating” a large number of DOM operations. Because the template engine is more of a point-to-point solution to cumbersome DOM manipulation, it is neither capable nor positioned to replace jQuery, and the two coexist in harmony. So there is no “template engine era”, only “template engine solution”.
Template syntax is basically a rule that combines JS and HTML together, and what a template engine does is very easy to understand. Read in the data source, stuff it into a preconfigured HTML template, then blend the two together and spit out a target string for you. The contents of this string, which is essentially a standard rendering HTML code, will correspond to a DOM element. Finally, mount the DOM element to the page, and the entire template rendering process is complete. Of course, the actual process is a little more complicated than we describe.
A template engine typically needs to do several things:
- Read HTML template and parse it, separate out the JS information;
- Splicing the parsed content into a string, dynamically generating JS code;
- Run dynamically generated JS code, spit out “target HTML”;
- Assigning the “target HTML” to the innerHTML triggers the rendering pipeline to complete the rendering of the actual DOM.
Using a template engine solution to render data takes the hassle out of manipulating a lot of DOM: every time the data changes, we don’t have to worry about where the data changed, and we don’t have to manually make point-to-point DOM changes. Just focus on the data and the data changes themselves, and the DOM level change template engine will do that for us.
Unfortunately, the template engine was created to separate the user interface from the business data, but the actual application scenario is basically limited to the point of “efficient string concatenation”, so you can’t expect it to do too complicated things. Especially unacceptable is its performance: it is not “smart” enough to update the DOM by deregistration of the entire DOM, and there is no update buffer. In the case of frequent DOM operations, the template engine may directly cause the page to freeze.
While the template engine isn’t a complete solution to front-end manipulation of the DOM, the idea is certainly highly advanced: it allows programmers to focus on data without having to worry about DOM details, just like React’s “data-driven view” idea.
So what to do?
The “predecessors” saw the promise in template engines. They decided to take the basic path of “data-driven view”, so they began to explore this idea: data-driven view solution for template engines. The core problem is that changes to the real DOM are too extensive, leading to DOM manipulation with too much scope and frequency, which can lead to poor performance. Then the guys thought: since the real DOM is such a performance drain, why don’t I just play with the fake DOM?
Further down the line, there’s the virtual DOM we all love.
How does the virtual DOM solve the problem
To separate the user interface from the data, the template engine does this:
With the help of the virtual DOM, things look like this:
The virtual DOM is not always implemented using templates. React, for example, uses JSX. JSX is not a template in nature, but a JS syntax sugar that has a similar experience to templates.
The difference is that there is an extra layer of virtual DOM as a buffer layer. The buffer layer is the good: when the DOM manipulation (rendering update) more frequently, it will first compare the two virtual DOM tree, pinpoint specific parts need to be updated, generate a “patch set”, only the “patch” to play on the need to update the part of the real DOM, achieve accurate “delta update”. The corresponding virtual DOM workflow for this process is shown below:
React uses the virtual DOM for better performance?
Throughout the evolution of DOM manipulation, the main contradiction was not about performance, but about how well the developer wrote it, and about the development experience/development efficiency. The virtual DOM is nothing more than a high-order product created by front-end developers in pursuit of better r&d experience and efficiency. The advantage of the virtual DOM is that it can provide a more streamlined and efficient mode of development (i.e., functional UI programming) while still maintaining decent performance.
Using the template rendering example discussed earlier, we can compare its performance overhead with that of the virtual DOM. The rendering workflow pairs for both are shown below:
As can be seen from the figure, step 1 of template rendering and step 1 and step 2 of virtual DOM rendering both belong to JS behaviors. The two are comparable. Let’s put them together: The process of dynamically generating HTML strings is essentially the stitching of strings, which has limited consumption on performance; However, the virtual DOM construction and diff process logic are relatively complex, which inevitably involves time-consuming operations such as recursion and traversal. So in terms of JS behavior, template rendering wins.
Step 3 of template rendering and step 3 of the virtual DOM are BOTH DOM-category actions and are comparable, so we can still happily compare them: template rendering is a full update, while the virtual DOM is a differential update.
At first glance it may seem that differential updates are definitely more efficient than full updates, but consider the case where the data content changes so much (or the whole thing changes) that the differential updates compute very close (or exactly the same) to the full updates.
In this case, the DOM update effort is basically the same, while the virtual DOM is accompanied by more expensive JS calculations. One of the things that can happen is that template rendering and the virtual DOM are very much the same in terms of overall performance: If the DOM updates calculated by the two are exactly the same, the virtual DOM is likely to lose to template rendering. But if the two are even slightly apart in the amount of final DOM manipulation, the virtual DOM will have the edge over template rendering. Because the disadvantage of virtual DOM mainly lies in the time consuming of JS computation, and the energy consumption of DOM operation and JS calculation are not of the same magnitude at all, the performance consumption of a very small amount of DOM operation is enough to support a large number of JS computation.
This situation is relatively extreme. In real development, the more common scenario is that I only modify a small amount of data each time I setState, such as a few properties in an object, or a few elements in an array. In such a scenario, the DOM manipulation gap between template rendering and the virtual DOM is completely opened, and the virtual DOM will have an absolute advantage in performance.
What is the value of the virtual DOM?
This is an open question, and there is no standard answer. I just want to discuss some common understandings about the virtual DOM from the perspective of “what are the key problems solved by the virtual DOM?”
The virtual DOM solves the following two key problems:
-
R&d experience/R&D efficiency issues
Behind each revolution in the DOM mode of manipulation is the front end’s further pursuit of efficiency and experience. The emergence of virtual DOM provides a highly available vehicle for the idea of data-driven view, enabling front-end development to achieve efficient declarative programming based on functional UI programming.
-
Cross-platform issues
The virtual DOM is a layer of abstraction from the actual rendered content. Without this layer of abstraction, the view layer is tightly coupled to the rendering platform, and you may have to write two or more completely different sets of code on the Web and Native side to describe the same view content. But now there is a layer of descriptive virtual DOM in the middle, which describes things that can be real DOM, iOS interface, Android interface, small programs…… The same set of virtual DOM can be connected with the rendering logic of different platforms, so as to achieve “once coding, multi-terminal operation”. In the final analysis, cross-platform is also a means of r&d and efficiency improvement, which is highly consistent with 1 in ideology.
The virtual DOM still has a lot to offer.
In addition to differential updates, batch updates are an important performance effort of the virtual DOM: Batch updates are handled by the Batch function in the common virtual DOM library. In the case of very fast differential updates (such as multiple operations on the SAME DOM in a very short period of time), the user really only sees the effect of the last update. In this scenario, the first few updates don’t make much sense, but they all trigger the re-rendering process, causing a lot of unnecessary and energy-intensive operations.
Batch is used to buffer the patch set generated each time. It will temporarily store the collected patch set in the queue, and then give the final result to the rendering function, and finally realize the centralized DOM batch update.