• Is Virtual DOM Derived From Document Fragments?
  • Originally written by Jennifer Fu
  • The Nuggets translation Project
  • Permanent link to this article: github.com/xitu/gold-m…
  • Translator: regon – cao
  • Proofread by zenblo Usualminds

The virtual DOM is a core concept of React. It is a representation of the UI that is kept in memory and synchronized with the actual DOM. The React DOM maintains the virtual DOM through local reconciliation differences, whose changes are based on the React pull plan and inserted into the actual DOM.

DocumentFragment is an interface that defines a minimal document object without a parent object. It is treated as a lightweight Document for storing DOM objects. Document fragments have no effect on the actual DOM, but child nodes can be inserted into the actual DOM as needed.

The virtual DOM and document fragments use the same ideas to improve UI performance. Does this show that the virtual DOM is derived from document fragments?

Let’s take a closer look at JavaScript, React, and Angular concepts.

What is DOM?

The Document Object Model (DOM) is a data object composed of the structure and content of web documents. The DOM is an interface to HTML and XML that allows you to modify document structure, style, and content. The DOM represents documents as nodes and objects so that programming languages such as JavaScript can access web pages.

Here is an example of a DOM tree:

Do you want to test the performance of a 1,000,000 node page load? Try the following index.html:

<! DOCTYPEhtml>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="container"></div>
    <script>
      function addChildren(container) {
        const startTime = new Date(a);for (let i = 0; i < 1000000; i++) {
          const child = document.createElement('div');
          child.innerText = i;
          container.appendChild(child);
        }
        const endTime = new Date(a);console.log(endTime - startTime); About 2514 ms / /
      }
      const container = document.getElementById('container');
      addChildren(container);
    </script>
  </body>
</html>
Copy the code

The load time varies a little from run to run, but is generally around 2.5 seconds.

DOM manipulation is expensive, and adding and removing elements results in redrawing and reordering of page content.

Why document fragments?

Document. CreateDocumentFragment () creates an empty DocumentFragment, can add will not be rendered DOM node in screen. After the off-screen DOM tree is created, the child nodes of the DocumentFragment can be updated to the real DOM as needed.

Because the document fragment is in memory and not part of the actual DOM, adding child elements to it does not cause page backflow (calculation of element position and geometry). Bulk updating of document fragments reduces the number of updates, which is more likely to improve performance.

Here is an example of how to use document fragments:

<! DOCTYPEhtml>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="container"></div>
    <script>
      function addChildren(container) {
        const startTime = new Date(a);for (let i = 0; i < 1000000; i++) {
          const child = document.createElement('div');
          child.innerText = i;
          container.appendChild(child);
        }
        const endTime = new Date(a);console.log(endTime - startTime); About 1223 ms / /
      }
      const container = document.getElementById('container');
      const fragment = document.createDocumentFragment();
      addChildren(fragment);
      const startTime = new Date(a); container.appendChild(fragment);const endTime = new Date(a);console.log(endTime - startTime); About 324 ms / /
    </script>
  </body>
</html>
Copy the code

The load time varies a little from run to run, but is generally around 1.5 seconds.

After the child element is inserted into the actual DOM (line 25), the document fragment becomes an empty object. If you want to reuse the updated content, clone the document fragment before inserting the DOM.

Interestingly, empty document fragments can be used to repeatedly build future updates.

How does the virtual DOM work?

Is the virtual DOM derived from document fragments?

The answer is no, the virtual DOM does not use any document fragments.

Of course, the virtual DOM comes from the concept of using fictional DOM to improve performance. But the virtual DOM is designed for large-scale updates. It can also be used in environments where the DOM does not exist, such as Node.js. React was the first mainstream framework to use the virtual DOM. In addition, Vue, Ember, Preact, and Mithril all use virtual DOM technology.

React has been using Fiber architecture since version 16.

“In an interaction, every update doesn’t need to be executed immediately; In fact, this is not worth the cost, as it results in lost frames and reduces the user experience.

Different updates have different priorities – for example, updates to animation should be done before updates to data.

The push based approach requires the application (and indeed the developer) to decide how to schedule updates. The React approach makes it smart to make decisions for you.” — React Fiber architecture on GitHub

React has several separate stages of deconfliction and rendering.

  • Dealing with differences: The React algorithm used to distinguish differences between two trees determines what needs to be changed. Different components are considered different trees. React doesn’t try to distinguish between them, but completely replaces the old tree. Lists are distinguished by keys. Bonds should be stable, predictable, and unique.
  • Rendering: The rendering process actually updates and renders the application based on the differences. It can split rendering work into chunks and spread the chunks over multiple frames. It uses a virtual stack frame to control work, prioritizing different types of work, repeating previously completed work, and terminating work that is no longer needed.

This split allows React DOM and React Native to use their own renderers while sharing the same coordinator.

Here is an example that shows the benefits of the virtual DOM. Here is the public/index.html modified from the Create React App:

<! DOCTYPEhtml>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="# 000000" />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
    <! -- manifest.json provides metadata used when your web app is installed on a user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/ -->
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <! -- Notice the use of %PUBLIC_URL% in the tags above. It will be replaced with the URL of the `public` folder during the build. Only files inside the `public` folder can be referenced from the HTML. Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will work correctly both with client-side routing and a non-root public URL. Learn how to configure a non-root public URL by running `npm run build`. -->
    <title>React App</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <span id="pureDom"></span>
    <span id="root"></span>
    <! -- This HTML file is a template. If you open it directly in the browser, you will see an empty page. You can add webfonts, meta tags, or analytics to this file. The build step will place the bundled scripts into the <body> tag. To begin the development, run `npm start` or `yarn start`. To create a production bundle, use `npm run build` or `yarn build`. -->
  </body>
</html>
Copy the code

Lines 30 and 31 are two span elements side by side.

Here is the modified SRC /app.js file, which returns a SELECT element. Starting with React 17, the React component can use JSX alone without introducing React.

function App() {
  return (
    <select>
      <option value="apple">Apple</option>
      <option value="pear">Pear</option>
    </select>
  );
}
export default App;
Copy the code

The following is a modified src/index.js:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

const updateRender = () = > {
  document.getElementById('pureDom').innerHTML = `  `;

  ReactDOM.render(
    <React.StrictMode>
      <App />
    </React.StrictMode>.document.getElementById('root')); };setInterval(updateRender, 1000);

// If you want to test performance in your application, you can pass a function to record the results (e.g. ReportWebVitals (console.log))
// Or send to the statistics port. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
Copy the code

Lines 8-13 render a SELECT element with the actual DOM.

Lines 15-20 render a select element using the virtual DOM.

Shown below are two select elements that select Apple side by side.

When the update is triggered at line 23, two SELECT elements are created every second.

Try to select Pear for two select.

The select on the left is rendered directly into the DOM, and frequent creation prevents Pear from being selected.

The select on the right is first rendered into the virtual DOM, where frequent creation takes place. Therefore, the SELECT element works.

React clips

To make things even more interesting, React creates the React.Fragment syntax, which wraps a set of child elements without introducing additional DOM nodes. Here is an example of a react. Fragment.

return (
  <React.Fragment>
    {new Array(1000000).fill(0).map((_, i) => (
      <div key={i}>{i}</div>
    ))}
  </React.Fragment>
);
Copy the code

React.Fragment can be shortened to an empty tag:

return (
  <>
    {new Array(1000000).fill(0).map((_, i) => (
      <div key={i}>{i}</div>
    ))}
  <>
);
Copy the code

Declare a React Fragment directly with \< react. Fragment> with the key. Here’s an example from the official document:

function Glossary(props) {
  return (
    <dl>{props.items. Map (item => (// React will issue a warning without 'key'<React.Fragment key={item.id}>
          <dt>{item.term}</dt>
          <dd>{item.description}</dd>
        </React.Fragment>
      ))}
    </dl>
  );
}
Copy the code

The React fragment has nothing to do with the document fragment except for their similar names.


What is incremental DOM?

The incremental DOM is a library that builds DOM trees and updates them correctly as the data changes. It differs from the virtual DOM in that no intermediate tree is created (it is modified directly in the real DOM). This approach significantly reduces memory allocation and repeated garbage collection for incremental UPDATES to the DOM tree, and therefore can significantly improve performance in some cases.

Incremental DOM removes extra copies of DOM. This reduces memory usage, but also slows down the speed of finding differences in the DOM tree. Reducing memory usage is crucial for mobile phones and other memory-constrained devices.

The incremental DOM is primarily used as a compilation target for templating languages such as Angular. Starting with release 9, Angular uses Angular Ivy, a compiler that uses the incremental DOM at run time.

Here’s an example from the official website:

function renderPart() {
  elementOpen('div');
    text('Hello world');
  elementClose('div');
}
Copy the code

The above code is converted to:

<div>
  Hello world
</div>
Copy the code

By calling renderPart above, patch can render the desired structure into an existing Element or Document.

patch(document.getElementById('someId'), renderPart);
Copy the code

What is shadowdom?

Shadow DOM is a technique for ensuring that code, style, and structure are encapsulated in a separate, hidden DOM tree. The shadow DOM can be attached to elements in the DOM. The shadow DOM is part of a Web component that uses a different set of techniques to create reusable custom elements.

Here is an example of a shadow DOM:

<! DOCTYPEhtml>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0" />
    <title>Document</title>
    <script>
      class NewNumberListElement extends HTMLElement {
        constructor() {
          super(a);const shadow = this.attachShadow({ mode: 'open' });
          for (let i = 0; i < 1000000; i++) {
            const child = document.createElement('div');
            child.innerText = i;
            shadow.appendChild(child);
          }
        }
      }

      customElements.define('new-number-list-element', NewNumberListElement);
    </script>
  </head>
  <body>
    <div id="container">
      <new-number-list-element />
    </div>
  </body>
</html>
Copy the code

Lines 8-18 define an element class using the shadow DOM. The class name is defined as a new label name, new-number-list-Element, and registered with window.customElements (line 20). Line 25 uses the newly created tag.

Before Angular Ivy, older compilers and run-time view engines used the shadow DOM.

conclusion

We’ve answered the question “Does the virtual DOM derive from document fragments?” That’s the question.

In this long list of answers, we explored DOMs, document fragments, virtual DOM, React fragments, incremental DOM, and shadow DOM. This knowledge is useful for our interviews and daily coding.

If you find any mistakes in your translation or other areas that need to be improved, you are welcome to the Nuggets Translation Program to revise and PR your translation, and you can also get the corresponding reward points. The permanent link to this article at the beginning of this article is the MarkDown link to this article on GitHub.


The Nuggets Translation Project is a community that translates quality Internet technical articles from English sharing articles on nuggets. The content covers Android, iOS, front-end, back-end, blockchain, products, design, artificial intelligence and other fields. If you want to see more high-quality translation, please continue to pay attention to the Translation plan of Digging Gold, the official Weibo, Zhihu column.