preface

Those who have done web know that web development is usually a simple user interaction. According to the user’s operation on the page, the user initiates a request to the remote server to obtain data, and then renders the data to the interface and presents it to the user. So the web front end is mostly about updating the DOM, removing the DOM, or adding the DOM to manipulate the DOM.

One of the things that made JQuery popular in the early days was that it provided a very convenient API for us to move around the DOM tree and manipulate the DOM. With the advent of AngularJS, which broke everything and introduced data binding, a bridge between data and interface was built through one-way binding or two-way binding. Front-end development moved from MVC to MVVM, data-driven development. These good ideas were later kept in React and Vue. The React UI JS library introduced the virtual DOM for the first time. So what is the virtual DOM?

What is the virtual DOM

A Virtual DOM is a Virtual DOM node. Nodes in the DOM are simulated by the ordinary Object Object of JS, and then rendered as real DOM nodes by the specific render method.

What are the benefits of the virtual DOM

What problems did the advent of the virtual DOM solve

In the browser, the DOM engine and JS engine are independent of each other, but they work on the same main thread. When JS calls the DOM API, the JS engine is suspended, the incoming parameter data is converted, the DOM engine is activated, the DOM is redrawn, the possible return values are converted, and the main thread is returned to the JS engine. For frequent DOM API calls, recalculation of the layout and redrawing of the image can cause greater performance costs because browser vendors don’t do “batch processing” optimization, which is why the virtual DOM appears

The difference between the virtual DOM and the real DOM

The virtual DOM will not be typesetted and redrawn immediately. The virtual DOM is frequently modified, and then the parts that need to be modified in the real DOM are compared and modified at one time. Finally, the virtual DOM is typesetted and redrawn in the real DOM to reduce excessive losses of DOM node typesetting and redrawing. The virtual DOM effectively reduces the redrawing and typesetting of the DOM because it ends up being different from the real DOM and can render only parts of it

Virtual Dom has patch algorithm, according to the comparison of old and new VNodes through optimization to find different nodes to repair and update. Direct overwrite DOM without violence.

implementation

  • Create a virtual DOM
  • Render the virtual DOM
  • Bind the virtual DOM to the page
  • Implement diff algorithm
  • Realize the patch is

Set up the environment

  • Create the project VDOM
  • Initialize the projectnpm init -y
  • Install project build tool dependenciesnpm install parcel-bundler(Optional) You can also start a service using the live-server tool
  • Create a SRC folder under the directory to hold the project files, then create index.html and main.js, and reference main.js in index.html
<! DOCTYPEhtml>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
    <title>Document</title>
</head>
<body>
    <div id="app"></div>
    <script src="main.js"></script>
</body>
</html>
Copy the code

Create a virtual DOM object in main.js, just a normal javascript object.

const vApp = {
  tagName: 'div',
  attrs: {
    id: 'app',
  },
};

console.log(vApp);
Copy the code
  • Configure the project JSON file and add a footstep to run the startup project
{..."scripts": {
    "dev": "parcel src/index.html",}... }Copy the code
  • Start the project
npm run dev
Copy the code

Implement the createElement method

src/vdom/createElement.js
export default (tagName, { attrs = {}, children = [] } = {}) => {
  return {
    tagName,
    attrs,
    children,
  };
};
Copy the code

Now let’s create the virtual DOM and give it to createElement.

import createElement from './vdom/createElement';

const vApp = createElement('div', {
  attrs: {
    id: 'app',}});console.log(vApp);
Copy the code
import createElement from './vdom/createElement';

const vApp = createElement('div', {
  attrs: {
    id: 'app',},children: [
    createElement('div', {
      attrs: {
        id:"container"}}),]});console.log(vApp);
Copy the code

Creating {a: 3} objects in literal form automatically inherits Object, which means that the created objects will have methods defined in Object.prototype, such as hasOwnProperty, toString, etc. We wanted to be a little more “pure” in creating javascript objects that represent the virtual DOM, so we came up with the following code,

Create (null) creates an ordinary Object that does not inherit from Object, but from NULL.

export default (tagName, { attrs, children }) => {
  const vElem = Object.create(null);

  Object.assign(vElem, {
    tagName,
    attrs,
    children,
  });

  return vElem;
};
Copy the code

The virtual DOM can also be written in JSX syntax, which is a JavaScript syntax extension. JSX describes UI well, which is more intuitive to write. JSX may be reminiscent of template languages, but JSX has all the functionality of JavaScript.

const element = (
	<div id="app">
		<div id="container"></div>
	</div>
);
Copy the code

JSX

createElement(“div”,{attrs:”id”})

Implement the Render method

The Render method is used to render virtual elements. The functions that generate the virtual DOM have been defined. The next step is to implement method one that converts the created virtual DOM into the real DOM. Define Render (vNode), which takes a virtual node as an argument and returns the corresponding DOM.

const render = (vNode) = > {
  // For example, create element
  // e.g. 
      
const $el = document.createElement(vNode.tagName); // Add attrs for the virtual DOM to the element // e.g.
for (const [k, v] of Object.entries(vNode.attrs)) { $el.setAttribute(k, v); } // Add child nodes // e.g.
for (const child of vNode.children) { $el.appendChild(render(child)); } return $el; }; export default render; Copy the code

The above code should be clear, so I won’t explain it too much

ElementNode and TextNode

In the real DOM, there are eight types of nodes, and we’ll consider only two of them today. ElementNode, such as

and
And TextNode TextNode, plain text. {tagName, attrs, children}, we also need to do some extra work in the Render method to be compatible with TextNode.

import createElement from './vdom/createElement';

const vApp = createElement('div', {
  attrs: {
    id: 'app',},children: [
    'Hello world'./ / TextNode said
    createElement('div', {
      attrs: {
        id:"container"}}),// represents ElementNode]});// represents ElementNode

console.log(vApp);
Copy the code

Redefine render (vNode). First, you need to determine whether a vNode is a string. If it is a string, use Document.createTextNode (string) to render textNode, otherwise call renderElem(vNode).

const renderElem = ({ tagName, attrs, children}) = > {
  // e.g. 
       
const $el = document.createElement(tagName); // e.g.
for (const [k, v] of Object.entries(attrs)) { $el.setAttribute(k, v); } // e.g.
for (const child of children) { $el.appendChild(render(child)); } return $el; }; const render = (vNode) = > { if (typeof vNode === 'string') { return document.createTextNode(vNode); } return renderElem(vNode); }; export default render; Copy the code
import createElement from './vdom/createElement';

const vApp = createElement('div', {
  attrs: {
    id: 'app',},children: [
    'Hello world'./ / TextNode said
    createElement('div', {
      attrs: {
        id:"container"}}),// represents ElementNode]});// represents ElementNode

const $app = render(vApp);
console.log($app);
Copy the code

Begin to render

<div id="app">
  Hello world
  <div id="container"></div>
</div>
Copy the code