preface

Have you noticed? The ideas of good solutions are the same: When you study the Rendering mechanism of Flutter, you will find that the Flutter Rendering layer is similar to the virtual DOM in React. When you look at how Weex works, alas, you will find the virtual DOM again, not to mention the virtual DOM at the heart of VUE’s responsive view.

So what does this virtual DOM do? Why do so many frameworks use it? What is the advantage in nature? This article will talk about front-end and mobile.

What is DOM? What is the virtual DOM?

DOM is a document tree, corresponding to the user interface control tree, usually refers to the rendering tree corresponding to HTML in Web development, but the generalized DOM can also refer to the control tree corresponding to XML layout in Android, and DOM manipulation refers to the direct operation of the rendering tree (or control tree).

The virtual DOM is a data structure used to represent the real DOM structure.

thinking

Back in the pre-school days, or jQuery days, want to assign? Change the style? The values? All are document.getelementById () clicks. What’s wrong with manipulating the DOM directly like this?

Problems with manipulating the DOM directly

1. Model and View coupling

One of the most intuitive problems is mixing the presentation logic of the user request with the business logic that the control layer is supposed to implement, and the two parts are very dependent.

2. Performance loss caused by high frequency operation

Write a simple Demo, and let’s see what happens.

Why is there a performance loss?

There are two reasons for this:

1. Cross-boundary AC loss

Think of DOM and ECMAScript as islands, each connected by a toll bridge. — High Performance JavaScript

DOM belongs to the rendering engine, JS belongs to the JS engine, and they are independent of each other in the browser kernel. Individually, both are fast, but when we use JS to manipulate the DOM, there is “cross-border communication” between the engines. This “cross-border communication” implementation is not simple, it relies on the bridge interface as a “bridge”, as shown below:

Since it is a toll bridge, you have to charge for crossing the bridge. Every time we manipulate the DOM, either to modify it or just to access its value, we have to bridge it. A large number of times can cause significant performance problems.

What about mobile hybrid development?

Take RectNative for example, RectNative is a set of cross-platform technology solution based on Native control (non-Web UI) business logic and JS. The control label written in JS is not a real control, which will be resolved into a Native control at the Native end. For example, the

tag corresponds to the TextView control in Android.

RN needs to communicate between JS and Native in the layout process. If it encounters sliding and dragging, the disadvantage is obvious, which is the same as the problem caused by frequent DOM manipulation by JS in the browser, which will bring considerable performance overhead.

2. DOM modification causes redrawing or rearrangement

Modifying DOM attributes is even more expensive, causing the rendering engine to recalculate geometric changes (rearranging and redrawing). Let’s look at the render steps:

During page generation, layout and rendering will be performed at least once. Subsequent user operations, if the DOM node is modified, will trigger the change of the Render Tree, thus performing steps 2, 3, 4, 5 above. Therefore, if there are many DOM operations in JS, it will constantly trigger the redraw or rearrangement. Affecting page performance.

On mobile, things aren’t much better.

Property changes to any View in a layout can cause a ripple effect if the control hierarchy is very complex. For example, a sudden doubling of the size of a BTN may cause the position of the sibling view to change, or the size of the parent view to change. When a large number of layout() operations are called and executed frequently, this can cause the View to be overrendered frequently, resulting in frame loss or UI stuttering.

The solution

In view of the above problems, we propose solutions one by one:

1. Reduce the number of crossing Bridges and merge operations

Every time ECMAScript accesses the DOM, it passes over the bridge and pays a “bridge fee,” which increases the more times it accesses the DOM. Therefore, the recommended course of action is to minimize the number of bridge crossings and try to stay on ECMAScript Island. — High Performance JavaScript

How do we reduce the number of times we cross the bridge? The frequency of bridge crossings is related to frequent DOM operations.

For example, the worst way to add data to a list is this:

  for (var i = 0; i < N; i++) {
        var li = document.createElement("li");
        li.innerHTML = arr[i];
        ul.appendChild(li);
    }
Copy the code

It’s going to manipulate the DOM N times and trigger N redraws. Rerendering is certainly unavoidable, and our goal is to minimize redrawing and reordering.

How about not manipulating the DOM right away?

Save the N updates to a JS object, and finally attach the JS object to the DOM tree once, notifying the browser to perform the drawing. In this way, no matter how complex the DOM operation is, it will only trigger the entire rendering process once, avoiding a lot of unnecessary calculation. (Ecstatic. JPG)

However, there are many ways to optimize the DOM without necessarily relying on the virtual DOM, so this is not the root cause of the need for the virtual DOM. The root cause is the need for responsiveness.

2. The response type

If you use JS to manipulate the DOM directly, there will be a mismatch between view data and model data. Can we let developers only care about state (data) changes, and not control operations? Of course you can!

React brings up an important idea: when the state changes, the UI automatically changes.

Refactoring the user interface each time the state changes, rerendering the entire view. If you don’t have the virtual DOM, you can simply reset the innerHTML. Resetting the innerHTML makes sense if most of the data is changed, but if only one row of data is changed, you’re obviously wasting a lot of money.

This is why we need the virtual DOM, to replace the developer’s manual work and ensure that only the actual DOM manipulation (partial refresh) is performed on the parts that actually change.

3. Summary

Any changes made by the developer to data and state are automatically and efficiently synchronized to the virtual DOM (automatic synchronization, responsive) and then batch synchronized to the real DOM, rather than manipulating the DOM for each change (batch synchronization, merge).

  1. Data-driven views without directly manipulating controls
  2. Minimize changes to the final view and improve page rendering efficiency

How to use the virtual DOM?

1. React

When React UI renders, it first renders a virtual DOM. This is a lightweight pure JS object structure that does not fully implement the DOM, but mainly preserves the hierarchical relationship between nodes and some basic properties. DOM is too complex to be concerned with in the final rendering process. Therefore, each node in the virtual DOM has only a few simple properties. Even if you delete the virtual DOM directly, it is very fast to create a new virtual DOM according to the data passed in.

When there are changes, a new virtual DOM is generated. This new virtual DOM reflects the new state of the data model. We now have two virtual DOM: the new one and the old one. Compare the differences of DOM trees to get a Patch, and type this Patch into the real DOM, which is a bit like the idea of version control to make patches.

So how do we tell the difference between two DOM trees? The Diff algorithm!

That is, given any two trees, find the least number of transformation steps. However, the standard Diff algorithm requires O(n^3) complexity, which clearly cannot meet the performance requirements. Facebook engineers made two simple assumptions about the Web interface that reduced the complexity of the Diff algorithm to O(n).

  1. Two identical components produce a similar DOM structure, and different components produce different DOM structures.
  2. A set of child nodes at the same level can be distinguished by a unique ID.

Algorithm optimization is the basis of Render in React interface. Facts also prove that these two assumptions are reasonable and accurate, ensuring the performance of the overall interface construction.

It is easy to infer from the processing logic of nodes of different types that the React DOM Diff algorithm actually only compares trees layer by layer, as shown in the following figure:

React will only compare DOM nodes in the same color box, that is, all children of the same parent node. When a node is found to no longer exist, the node and its children are removed completely and are not used for further comparison. This allows you to compare the entire DOM tree with only one walk through the tree.

In practice, Diff algorithm is not so simple, interested partners can go to the end of the tweet to understand.

What about cross-platform solutions?

2. RN

As mentioned above, RN is a derivative of React on the native mobile application platform. What are the main differences between the two? The main difference is what the object of the virtual DOM map is. The React virtual DOM is eventually mapped to the browser DOM tree, while the RN virtual DOM is mapped to the native control tree via JavaScriptCore.

The steps are as follows:

  1. Layout messaging: Pass virtual DOM layout information to native;
  2. Native according to the layout information, mapping to the corresponding native control tree, rendering control tree.

RN is now cross-platform.

3. weex

Weex to a certain extent with JS vUE unified the world effect.

As you can see, WEEX compiles and builds the virtual DOM and sends rendering instructions to the RenderEngine layer, so that the same JSON data can be rendered into different versions of the UI in different rendering engines. This is why WEEX can be dynamic.

The syntax of the three terminals is different. How is Weex unified? Focus on JS Framework!

Weex on the basis of RN JS V8 engine, more JS Framework undertakes important responsibilities, it is mainly responsible for: management of weeX life cycle; Parse THE JS Bundle, convert it into a Virtual DOM, and build the page from the platform’s different APIS; Two-way data interaction and response.

This allows for uniformity at the upper level, with code patterns, compilation processes, template components, data binding, life cycles, and so on being consistent throughout development. Thanks to the unity of the upper layer, it only needs to determine whether vue. JS generates real DOM or renders components through Native Api in the final judgment of JS Framework layer.

4. Flutter

RN and React work the same way. What about Flutter? The central idea of Flutter Widgets is to build your UI (non-native controls) with widgets. The communication loss between the native control layer and the JS layer is reduced, and the virtual DOM is not needed, right?

Not too! The Flutter Widget takes inspiration from React and is also built with a modern responsive framework.

Take a look at three important trees in Flutter first:

  • Widget tree: A control tree that represents the structure of the control we wrote in the DART code, but this is just descriptive information that is not recognized by the rendering engine.

    The Widget is configured with properties that define its presentation, such as the string that the Text component needs to display, the content that the input box component needs to display… The Element tree records this configuration information.

  • Element number: Actual control tree

    The widgets displayed on the phone screen are not the widgets we wrote in our code. Flutter generates the corresponding Element tree based on the Widget tree information. In Flutter, one Widget can be reused multiple times for multiple Element instances. Element is what we actually display on the screen.

    Another difference between an Element and a Widget is that a Widget is immutable, and changes to it mean that it has to be rebuilt, which is very frequent. If we give it more work, it will have a very high performance cost. Therefore, we treat the Widget tree as a virtual DOM tree. The Element rendered on the screen is ElememtTree, which holds a reference to its Widget. If the Widget changes, it is marked as a dirty Element, and the next time the view is updated, only the modified content will be updated based on this state. This associates mutable state with widgets to improve performance.

  • RenderObject tree: A rendering tree that does component layout rendering and contains information about rendering collocations, layout constraints, and so on.

In short, the purpose of Flutter is to introduce the virtual DOM in order to determine the minimum changes needed to transition the underlying render tree from one state to the next.

What the virtual DOM means for cross-platform technology

What have you learned about the virtual DOM after analyzing the various cross-platform technologies?

Why use the virtual DOM?

Is it fast? (Actually not necessarily fast)

Is it decoupling?

Is it reactive?

More important for cross-platform technologies are:

The virtual DOM is a lightweight representation of the DOM in memory, and a common convention! You can use different rendering engines to generate UI for different platforms!

The virtual DOM is very portable, which means you can render to any end outside the DOM, and there’s a lot you can do with your imagination.

Look again at the virtual DOM

The real value of the virtual DOM has never been performance, but rather the ability to update the DOM with minimal cost, regardless of how the data changes, and masking the underlying DOM manipulation, allowing you to describe your purpose in a more declarative way that makes your code more maintainable.

The virtual DOM brings out a lot of good ideas and opens the door to interesting architectures, such as treating views as state functions. It lets us write code as if we were rerendering the entire scene. It got me thinking, there is nothing that middleware can’t solve, if there is, then add more middleware.

Five words to summarize what it means:

Maintainability, minimum cost, efficiency, functional UI, data-driven

Think further

The explanation of the virtual DOM is over, but thinking about it is far from over.

The Rect approach has two major drawbacks:

  1. Every data change, even a small one, generates a complete virtual DOM, which can be a waste of computing resources if the DOM is complex.

  2. The process of comparing virtual DOM differences is slow and error-prone. Because the old and new virtual DOM held by React are independent of each other, React does not know what is happening to the data source and can only guess what needs to be done based on the two virtual DOM. The automatic guessing algorithm was inaccurate and slow, requiring the front-end developer to manually provide key attributes and some additional method implementations to help React guess correctly.

So?

Question: how does Vue use the virtual DOM? How to improve the above shortcomings? You can find out.

Want to know more?

  • Stop talking about virtual DOM. You’re gonna get punched in the face
  • React is faster than the REAL DOM.
  • Virtual DOM and Diff algorithms – Entry level
  • React virtual DOM and diff algorithm
  • In-depth analysis: How to implement a Virtual DOM algorithm
  • Explain the DIff algorithm of VUE
  • Analyze the diff algorithm of vue2.0

This piece took 26 tomato minutes (650 minutes)


I’m FeelsChaotic, a programmer who can write code, cut video and draw pictures. I’m committed to the pursuit of code elegance, architecture design and T-shaped growth.

Feel free to follow FeelsChaotic’s short books and nuggets, and if my articles are even remotely helpful to you, please feel free to ❤️! Your encouragement is my biggest motivation to write!

The most important, please give your suggestions or opinions, there are mistakes please correct!