Simple implementation of mini-vue rendering

Simple implementation of mini-vue rendering

Preface

The current mainstream frameworks Vue and React are both implemented through Virtual Dom, which improves page rendering efficiency through Virtual Dom technology. In Vue, we write HTML code in the template, and in React, we write HTML code in an internal render function. After this function is compiled through jsx, it will actually output an h function, which is our Virtual Dom. The following is a simple implementation of a virtual dom rendering real dom, as well as the update method.

Target

It mainly realizes the following three functions:

  • Return Vnodes through h function;
  • Use the mount function to mount the virtual dom onto the real node;
  • The patch function is used to update the DOM by comparing newVnodes with oldVnodes.

first step:

Create a node with the id app in the body tag. The virtual node will be mounted on this node later, and renderer.js is used to implement the above three functions.

<body>
  <div id="app"></div>
  <script src="./renderer.js"></script>
</body>

Step 2:

Write the h function to return tag (tag element), props (property object), and children (child nodes). In simple terms, the virtual Dom is an ordinary javascript object.

// renderer.js
 
const h = (tag, props, children) => {
  return {
    tag,
    props,
    children
  }
}

So through this h function, let's take a quick look at what the following code will output?

const vdom = h("div", {class: "header"}, [
  h("h2", null, "Hello World"),
  h("h2", {id: 0}, h("span", null, "啦啦啦啦")) // When props has no value, a null must be passed, it cannot be omitted]);
console.log(vdom);

As can be seen from the figure below, through the h function, a javascript object is returned to us, which is the Virtual Dom, forming a tree node.

So how do we mount this vnodes to the real node? Now let’s look at step three.

Step 3:

We first create a mount function, which needs to pass in two parameters. The first is the vnodes we just returned through the h function, and the second parameter is which node we need to mount these vnodes on. Let's look at the code:

const mount = (vnodes, app) => {
  // Create a node through the tag value in vnodes, such as ("div", "h2") // Also save a real DOM in the vnodes object to facilitate future updates, additions, etc. const el = vnodes.el = document.createElement(vnodes.tag); 
  // After getting this node, we add properties by judging the props value if (vnodes.props) {
    for (let key in vnodes.props) {
      // Here, after getting the key value in props, we make a judgment let value = vnodes.props[key];
      if (key.startsWith('on')) {
        // For example, if the user writes onClick="changeData", it is processed as an event listener function el.addEventListener(key.slice(2).toLowerCase(), value)
      } else {
        el.setAttribute(key, value)
      }
      // There are some judgments below, such as instructions, v-if, etc., for boundary processing}
  }
  // After processing props, the last node is Children if (vnodes.children) {
    if (typeof vnodes.children === 'string') {
      // If this is a string type, then you can add it directly to the node el.textContent = vnodes.children
    } else {
      // This case is an array type, containing child nodes, and then generates child nodes through traversal vnodes.children.forEach(vnode => {
        // Recursively mount the child node to the current node again mount(vnode, el)
      })
    }
  }
  //Finally mount this real node to the app node we passed in app.appendChild(el);
}
const app = document.querySelector("#app")
mount(vdom, app)

Let's take a look at the actual effect of mounting through the mount function:

So far, we have created a real DOM through a virtual DOM. How do we update these DOMs in Vue? Next, we create a patch function (update):

Step 4:

Through the patch function, we need to pass in two parameters (vnodes1, vnodes2), which are the new virtual dom and the old virtual dom. By comparing the new and old virtual doms, we can specify which nodes to update. (The key value is not considered here. If you need to refer to the key value, you can check the link: https://www.jb51.net/article/219078.htm)

//patch function n1: old node, n2: new node // In the vue source code, the old vnode and the new vnode are represented by n1 and n2 respectively const patch = (n1, n2) => {
  // Above we added the node attribute el to n2 through the mount function and bound it to n2 const el = n2.el = n1.el
  // First, start with the tags in the two if (n1.tag == n2.tag) {
    // n1 and n2 have the same tag, then compare props
    const n1Props = n1.props || {};
    const n2Props = n2.props || {};
    // Get props from n1 and n2 respectively and compare for (let key in n2Props) {
      // Take out all keys in n2 and check whether the key value of n2 is the same as the key value of n1 const n1Value = n1Props[key] || '';
      const n2Value = n2Props[key] || '';
      if (n1Value !== n2Value) {
        if (key.startsWith('on')) {
          // For example, if the user writes onClick="changeData", it is processed as an event listener function el.addEventListener(key.slice(2).toLowerCase(), n2Value)
        } else {
          el.setAttribute(key, n2Value)
        }
      }
      // If they are the same, no processing will be done}
    for (let key in n1Props) {
      const oldValue = n1Props[key];
      if (!(key in n2Props)) {
        if (key.startsWith('on')) {
          el.removeEventListener(key.slice(2).toLowerCase(), oldValue)
        } else {
          el.removeAttribute(key)
        }
      }
    }
  } else {
    // Tag is different, get the parent node of n1 const n1Parent = n1.el.parentElement;
    // Remove the old node from the parent node through removeChild, and then mount n2 to the parent node n1Parent.removeChild(n1.el); //n1.el is the real dom node added to the object through the mount function mount(n2, n1Parent)
  }
  // Finally, process children, which is relatively complicated. // Children can be strings or arrays, so let's look at how to process the string first. const n1Children = n1.children || [];
  const n2Children = n2.children || [];
  if (typeof n2Children === "string") {
    // If the new node content is a string, directly use innerhtml to replace el.innerHtml = n2Children;
  } else {
    // The following situation is when n2.children is an array if (typeof n1.children === "string") {
      // n1.children is a string, n2.children is an array el.innerHtml = ''; // First check the node content, then add new content mount(n2.children, el)
    } else {
      // When both are array types, the key value is not considered here const minLength = Math.min(n1Children.length, n2Children.length);
      for (let i = 0 ; i < minLength ; i++) {
        patch(n1Children[i], n2Children[i]);
      }
      if (n2Children.length > n1Children.length) {
        n2Children.slice(minLength).forEach(item => {
          mount(item, el)
        })
      }
      if (n2Children.length < n1Children.length) {
        n1Children.slice(minLength).forEach(item => {
          el.removeChild(item.el)
        })
      }
    }
  }
}

The above is a simple implementation of the patch function, which is actually what we call the diff algorithm (of course, the key value is not considered here, and only two can be compared in sequence), and the comparison is made at the same level. Now let's simulate and demonstrate whether the update can be successful:

const vdom = h("div", {class: "header"}, [
  h("h2", null, "Hello World"),
  h("h2", {id: 0}, [h("span", null, "啦啦啦啦")]) // When props has no value, a null must be passed, it cannot be omitted]);
const app = document.querySelector("#app")
mount(vdom, app)
setTimeout(()=> { // Pass the new and old Vnodes to patch after 3 seconds
  const vdom1 = h("div", {class: "header"}, [
    h("h3", null, "Hello World"),
    h("span", null, "哈哈哈哈")
  ])
  patch(vdom, vdom1)
},3000)

From the figure below, we can see that the virtual DOM update node has been simply implemented.

Summarize

It simply implements the virtual Dom to generate real nodes, and then updates them through patches. If you look at the source code again, you can better understand how Vue's renderer is implemented.

This is the end of this article about the simple implementation of mini-vue rendering. For more relevant mini-vue rendering content, please search for previous articles on 123WORDPRESS.COM or continue to browse the following related articles. I hope everyone will support 123WORDPRESS.COM in the future!

You may also be interested in:
  • Vue+ElementUI realizes the method of dynamic rendering and visual configuration of forms
  • A brief discussion on v-for loop rendering in vue.js
  • Vue calls the method after the page data rendering is completed
  • Vue implements scroll bar position control after rendering data (recommended)
  • Solve the problem that Vue modifies the array through the following table and the page does not render

<<:  Jmeter connects to the database process diagram

>>:  Method and introduction of table index definition in MySQL

Recommend

How to use file writing to debug a Linux application

In Linux, everything is a file, so the Android sy...

Application example tutorial of key in Vue page rendering

introduction During the front-end project develop...

Summary of MySQL basic common commands

Table of contents MySQL basic common commands 1. ...

5 commonly used objects in JavaScript

Table of contents 1. JavaScript Objects 1).Array ...

Dynamic SQL statement analysis in Mybatis

This article mainly introduces the dynamic SQL st...

Mysql 5.6 "implicit conversion" causes index failure and inaccurate data

background When performing a SQL query, I tried t...

Echarts legend component properties and source code

The legend component is a commonly used component...

Implementation of FIFO in Linux process communication

FIFO communication (first in first out) FIFO name...

DIV common attributes collection

1. Property List Copy code The code is as follows:...

How to use Nexus to add jar packages to private servers

Why do we need to build a nexus private server? T...

Detailed explanation of MySQL slow queries

Query mysql operation information show status -- ...

Website design should pay attention to the sense of color hierarchy

Recently I have been saying that design needs to h...

Detailed explanation of prototypes and prototype chains in JavaScript

Table of contents Prototype chain diagram Essenti...