How to quickly implement a virtual DOM system

Time:2022-5-3

Virtual DOM is one of the technical cores of the mainstream front-end framework. This paper describes how to implement a simple virtual DOM system.

Why do I need virtual DOM?

Virtual DOM is a tree composed of virtual nodes, which shows the structure of real dom. These virtual nodes are lightweight and stateless, usually strings or JavaScript objects that contain only the necessary fields. Virtual nodes can be assembled into a node tree. The two node trees are compared through a specific “diff” algorithm to find out the subtle change points, and then updated to the real dom.

The reason why there is a virtual DOM is that it is very expensive to update the real DOM directly. Compare the virtual DOM with the new one, and then update only the changed part to the real dom. Doing so is to operate pure JavaScript objects, avoiding direct operation of DOM as much as possible, and the reading and writing cost is much lower.

How to implement virtual DOM

Before we start, we need to clarify what necessary components should be included in a virtual DOM system?

First, we need to define what is a virtual node. A virtual node can be an ordinary JavaScript object or a string.

We define a functioncreateNodeTo create a virtual node. A virtual node contains at least three information:

  • tag: save the tag name and string of the virtual node
  • props: save the properties / attributes of the virtual node, ordinary object
  • children: save the child nodes of the virtual node, array

The following code iscreateNodeImplementation example:

const createNode = (tag, props, children) => ({
  tag,
  props,
  children,
});

We passcreateNodeYou can easily create virtual nodes:

createNode('div', { id: 'app' }, ['Hello World']);

//Return as follows:
{
  tag: 'div',
  props: { id: 'app' },
  children: ['Hello World'],
}

Now, we need to define acreateElementFunction to create a real DOM element from a virtual node.

staycreateElementIn, we need to create a new DOM element, then traverse the props attribute of the virtual node, add the attribute to the DOM element, and then traverse the children attribute. The following code is an implementation example:

const createElement = vnode => {
  if (typof vnode === 'string') {
    return document. createTextNode(vnode); //  If it is a string, the text element is returned directly
  }
  const el = document.createElement(vnode.tag);
  if (vnode.props) {
    Object.entries(vnode.props).forEach(([name, value]) => {
      el[name] = value;
    });
  }
  if (vnode.children) {
    vnode.children.forEach(child => {
      el.appendChild(createElement(child));
    });
  }
  return el;
}

Now, we can passcreateElementThe virtual node is transformed into a real dom.

createElement(createNode("div", { id: "app" }, ["Hello World"]));

//Output: Hello World

Let’s define another onediffFunction to implement the ‘diff’ algorithm. thisdiffThe function receives three parameters: an existing DOM element, an old virtual node and a new virtual node. In this function, we will compare two virtual nodes and replace the old elements when necessary.

const diff = (el, oldVNode, newVNode) => {
  const replace = () => el.replaceWith(createElement(newVNode));
  if (!newVNode) return el.remove();
  if (!oldVNode) return el.appendChild(createElement(newVNode));
  //Handling plain text
  if (typeof oldVNode === 'string' || typeof newVNode === 'string') {
    if (oldVNode !== newVNode) return replace();
  } else {
    //Compare tag names
    if (oldVNode.tag !== newVNode.tag) return replace();
    //Compare props
    if (!oldVNode.props?.some((prop) => oldVNode.props?.[prop] === newVNode.props?.[prop])) return replace();
    //Compare children
    [...el.childNodes].forEach((child, i) => {
      diff(child, oldVNode.children?.[i], newVNode.children?.[i]);
    });
  }
}

In this function, we first deal with the case of plain text. If the old and new strings are different, they will be replaced directly. After that, we can assume that both virtual nodes are objects. Let’s compare whether the tag names of the two nodes are the same, and replace them directly if they are different. Then compare whether the props of the two nodes are the same, and replace them directly if they are different. Finally, we use recursiondiffFunction to compare the children of two virtual nodes.

So far, we have realized all the functions necessary for a simple virtual DOM system. The following is an example:

const oldVNode = createNode("div", { id: "app" }, ["Hello World"]);
const newVNode = createNode("div", { id: "app" }, ["Goodbye World"]);
const el = createElement(oldVNode);
// Hello World

diff(el, oldVNode, newVNode);
// el will become: Goodbye World

The implementation in this paper focuses on the implementation principle of virtual DOM, and other factors such as performance are not considered in the implementation code.

Welcome to follow the official account “Zhongli Qianxun” or in mywebsiteBrowse for more systematic information.

Recommended Today

Big data Hadoop — spark SQL + spark streaming

catalogue 1、 Spark SQL overview 2、 Sparksql version 1) Evolution of sparksql 2) Comparison between shark and sparksql 3)SparkSession 3、 RDD, dataframes and dataset 1) Relationship between the three 1)RDD 1. Core concept 2. RDD simple operation 3、RDD API 1)Transformation 2)Action 4. Actual operation 2)DataFrames 1. DSL style syntax operation 1) Dataframe creation 2. SQL […]