Virtual Dom Details-

Keywords: Javascript React Vue Attribute

With the advent of Vue and React and the growth of many frameworks, virtual DOM has gradually become a topic we often discuss and discuss.What is virtual DOM, how virtual DOM is rendered, what is the difference between virtual Dom of Vue and virtual DOM of React, etc.... A series of topics are under discussion.For this reason, I also made some simple jokes about what is virtual DOM?

What is a virtual Dom

Virtual Dom was first introduced and used by React framework. Its excellent performance was quickly recognized by many developers. After React, vue2.0 introduced the concept of virtual DOM in its core.When there is no virtual DOM, we usually use HTML tags to build our pages one by one. Since there are DOM nodes, why not use the native DOM directly, then what are the drawbacks of the native DOM?This is because a Node node in a native DOM has more than N attributes. The core issue that affects page performance when operating on a DOM is that the DOM operation results in page redrawing or rearrangement. To reduce the impact of redrawing and rearrangement on page performance, do your best in any projectA major focus of performance optimization is to reduce the number of DOM nodes to operate on.

What is the so-called virtual DOM?This is to describe a piece of HTML code in the JavaScript language.It's easy to describe a piece of HTML code using JavaScript:

HTML:

<div class="" id="app">
  <p class="text">Node One</p>
</div>

JavaScript:

const createElement = () => {
  return {
    "tag":"div",
    "prop":{
      "id":"app"
    },
    "children":[
      {
        "tag":"p",
        "prop":{
          "class":"text"
        },
        "children":["Node One"]
      }
    ]
  }
}

In the above code, just simply use the JavaScript language to describe the corresponding code in the HTML section, at this time we just need to write another way to create the DOM, add the created DOM hierarchically to the inside page as described in the document.

The data type described in the JavaScript above can also be simply interpreted as a virtual DOM. Although this virtual DOM is so simple, it can be said that when a page needs to be rendered and updated like Vue and React, the difference between the pre-update and post-update virtual DOM is only partially different.Row update greatly reduces DOM operations.This is what we often call the DIFF algorithm.

From the above description, it can be concluded that because the properties and methods in the native DOM node are too complex and affect performance too much, Object is used to describe the HTML structure in the page to improve performance.

How to create a virtual DOM

If you are familiar with Vue or React, you may know one thing. First of all, let's talk about Vue. The virtual DOM in Vue in use is accomplished by template, which is the template we write most in our project. Vue compiles and processes it by vue-loader to form the virtual DOM we need, but not in React.In this way, React does not have a template, and React uses JSX pairs to compile and produce a virtual DOM, which is what either Vue or React ultimately wants.

If you want to know how a virtual DOM is created, you can simply implement its creation process. In the above, you can get a data text describing the DOM node, which we can create according to its needs:

const vnodeTypes = {
  //  HTML Node Type
  "HTML":"HTML",
  //  Text type
  "TEXT":"TEXT",
  //  Component type
  "COMPONENT":"COMPONENT"
};
const childTeyps = {
  //  Is empty
  "EMPTY":"EMPTY",
  //  single
  "SINGLE":"SINGLE",
  //  Multiple
  "MULTIPLE":"MULTIPLE"
};
//  New Virtual DOM
//    Label Name Creation Required
//    Label Properties
//    Label child elements
function createElement (tag,data,children = null){
  //  Label type of current element
  let flag;
  //  Label type for child elements
  let childrenFlag;
  if(typeof tag === "string"){
    //  If it is text, it is assumed that the normal HTML tag
    //  Set flag of its elements to HTML type
    flag = vnodeTypes.HTML;
  }else if(typeof tag === "function"){
    //  If it is a function, it is considered a component
    flag = vnodeTypes.COMPONENT;
  }
  else {
    //  Otherwise, it is a text type
    flag = vnodeTypes.TEXT;
  };
  //  Judging child elements
  if(children === null){
    //  If children is empty
    //  Then the child element type is empty
    childrenFlag = childTeyps.EMPTY;
  }else if (Array.isArray(children)){
    //  If children is an array
    //  Get child element length
    let len = children.length;
    //  If Length Exists
    if(len){
      //  Set the child element type to multiple
      childrenFlag = childTeyps.MULTIPLE;
    }else{
      //  Otherwise set to null
      childrenFlag = childTeyps.EMPTY;
    }
  }else {
    //  If it exists and is not empty
    //  Then set to single
    childrenFlag = childTeyps.SINGLE;
    //  Create a text type method and convert the value of children to a string
    children = createTextVNode(children+"");
  }

  //  Return virtual DOM
  return {
    flag, //  Virtual DOM Type
    tag,  //  Label
    data, //  Virtual DOM Properties
    children, //  Virtual DOM Child Node
    childrenFlag,  //  Virtual DOM Child Node Type
    el:null   //  The parent of a mounted element
  };
};

//  New Text Type Virtual DOM
function createTextVNode (text){
  return {
    //  Set node type to text
    flag:vnodeTypes.TEXT,
    //  Set to No Label
    tag:null,
    //  No attributes
    data:null,
    //  Subelement type set to single
    childrenFlag:childTeyps.EMPTY
  };
};

Creating a virtual DOM can be accomplished simply by calling createElement and passing in an object describing the virtual DOM to print out the created virtual DOM node:

const VNODEData = [
    "div",
    {id:"test"},
    [
        createElement("p",{},"Node One")
    ]
];
let div = createElement(...VNODEData);
console.log(div);

Result:

{
    "flag": "HTML",
    "tag": "div",
    "data": {
        "id": "test"
    },
    "children": [{
        "flag": "HTML",
        "tag": "p",
        "data": {},
        "children": {
            "flag": "TEXT",
            "tag": null,
            "data": null,
            "childrenFlag": "EMPTY"
        },
        "childrenFlag": "SINGLE"
    }],
    "childrenFlag": "MULTIPLE"
}

Printed from the above method is the object describing the virtual DOM according to the incoming, has created a virtual DOM tree, it is not a magical thing, in fact, there is no particularly important logic to carefully look at the code, it is just a change of data structure (can be understood, but can not be so external)Say, it's humiliating, haha).

Now that the virtual DOM node is out, the next step is how to render the virtual DOM. Rendering the virtual DOM requires a specific method. In Vue and React, there will be a real DOM node in HTML with the id app, which will be replaced by the real DOM node generated by the virtual DOM node when rendering. Then follow this stepThis idea continues to be implemented. Both Vue and React have render functions, which are also used here for naming. Before you start, first of all, make sure that whether the first rendering or updating is done through render functions, so you need to judge them, and the rest will not be further described.

//  Render Virtual DOM
//    Virtual DOM Node Tree
//    Container that hosts DOM nodes, parent element
function render(vnode,container) {
  //  First render
  mount(vnode,container);
};
//  First render
function mount (vnode,container){
  //  Required Rendering Label Type
  let {flag} = vnode;
  //  If it is a node
  if(flag === vnodeTypes.HTML){
    //  Call the Create Node method
    mountMethod.mountElement(vnode,container);
  } //  If it is text
  else if(flag === vnodeTypes.TEXT){
    //  Call Create Text Method
    mountMethod.mountText(vnode,container);
  };
};
//  Methods for creating various elements
const mountMethod = {
  //  Create HTML Element Method
  mountElement(vnode,container){
    //  Attribute, Label Name, Child Element, Child Element Type
    let {tag,children,childrenFlag} = vnode;
    //  True Node Created
    let dom = document.createElement(tag);
    //  Save the real DOM node in VNode
    vnode.el = dom;
    //  If not empty, child elements exist
    if(childrenFlag !== childTeyps.EMPTY){
      //  If it is a single element
      if(childrenFlag === childTeyps.SINGLE){
        //  Pass in the child element and the currently created DOM node as the parent element
        //  It's actually about mounting children into the currently created element
        mount(children,dom);
      } //  If more than one element
      else if(childrenFlag === childTeyps.MULTIPLE){
        //  Loop child nodes and create
        children.forEach((el) => mount(el,dom));
      };
    };
    //  Add Element Node
    container.appendChild(dom);
  },
  //  Create Text Element Method
  mountText(vnode,container){
    //  Create Real Text Node
    let dom = document.createTextNode(vnode.children);
    //  Save dom
    vnode.el = dom;
    //  Add Elements
    container.appendChild(dom);
  }
};

With the code above, you can render the real DOM, but this is only a small part of it.But many things have not been added, such as dynamically adding style styles, binding styles to elements, adding class es, etc. A series of problems have not been solved, and now the work is just a simple initialization.In fact, it is not difficult to accomplish the above functions. To know that everything just mentioned is added to the DOM node, we only need to do articles on the DOM node to improve the mountElement method:

const mountMethod = {
  //  Create HTML Element Method
  mountElement(vnode,container){
    //  Attribute, Label Name, Child Element, Child Element Type
    let {data,tag,children,childrenFlag} = vnode;
    //  True Node Created
    let dom = document.createElement(tag);
    //  Add attribute (_) updated here oh
    data && domAttributeMethod.addData(dom,data);
    //  Save the real DOM node in VNode
    vnode.el = dom;
    //  If not empty, child elements exist
    if(childrenFlag !== childTeyps.EMPTY){
      //  If it is a single element
      if(childrenFlag === childTeyps.SINGLE){
        //  Pass in the child element and the currently created DOM node as the parent element
        //  It's actually about mounting children into the currently created element
        mount(children,dom);
      } //  If more than one element
      else if(childrenFlag === childTeyps.MULTIPLE){
        //  Loop child nodes and create
        children.forEach((el) => mount(el,dom));
      };
    };
    //  Add Element Node
    container.appendChild(dom);
  }
};
//  dom add attribute method
const domAttributeMethod = {
  addData (dom,data){
    //  Mount Properties
    for(let key in data){
      //  dom node, property name, old value (easy to update), new value
      this.patchData(dom,key,null,data[key]);
    }
  },
  patchData (el,key,prv,next){
    switch(key){
      case "style":
        this.setStyle(el,key,prv,next);
        break;
      case "class":
        this.setClass(el,key,prv,next);
        break;
      default :
        this.defaultAttr(el,key,prv,next);
        break;
    }
  },
  setStyle(el,key,prv,next){
    for(let attr in next){
      el.style[attr] = next[attr];
    }
  },
  setClass(el,key,prv,next){
    el.setAttribute("class",next);
  },
  defaultAttr(el,key,prv,next){
    if(key[0] === "@"){
      this.addEvent(el,key,prv,next);
    }
    else {
      this.setAttribute(el,key,prv,next);
    }
  },
  addEvent(el,key,prv,next){
    if(next){
      el.addEventListener(key.slice(1),next);
    }
  },
  setAttribute(el,key,prv,next){
    el.setAttribute(key,next);
  }
};

The above simple implementation of the creation of virtual DOM and the mounting of attributes and events is a big leap forward, but the completion of initialization is not enough, it needs further processing, so~will continue to explain the update of virtual DOM when time is available.That is its DIFF algorithm part.Single responsibility, a blog only does one thing, haha~

summary

Virtual DOM is used as a core part in several popular frameworks, so its performance is efficient. This paper just makes a simple analysis. In the end, virtual DOM is to use JavaScript object to represent the information and structure of DOM tree. This JavaScript object can build a real DOM tree..When the state changes, the differences between the two trees are recorded by comparing the newly rendered JavaScript objects with the JavaScript objects of the old virtual DOM.Reflect the differences to the real DOM structure. When you finally manipulate the real DOM, you can only manipulate the differences.

See you next time. If there are any errors, ask the big guys to point out in time. If there are any errors in the article, leave a message in the comments area and I will make corrections as soon as possible.

Posted by lkq on Tue, 06 Aug 2019 11:07:28 -0700