In layman's language comprehension virtual Dom

 What is a virtual Dom?

Virtual Dom is a virtual DOM, JS we use to simulate the DOM structure, the structure is similar to the following code:

{
    tag:'ul',
    attrs:{
        id:'list'
    }
    children:[
    {
        tag:'li',
        attrs:{className:'item'},
        children:['item 1']
    },
    {        tag:'li',
        attrs:{className:'item'},
        children:['item 2']
    }
  ]
}复制代码

This code is such a simulation DOM structure

<ul>
    <li class='item'>item 1</li>
    <li class='item'>item 2</li></ul>复制代码

So why is there VDOM (virtual dom referred to) such a structure?


Why Virtual DOM?

Let's simulate such a scenario needs.

1. There are a bunch of data, data needs to be rendered into a form

2. tinker with a message, change table will follow

If not VDOM, we will use this code to complete the requirements

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8"> 
   <title>Document</title></head><body>
    <div id="container"></div>

    <button id="btn-change">change</button>
    <script type="text/javascript" src="https://cdn.bootcss.com/jquery/3.2.0/jquery.js"></script>
    <script type="text/javascript">
        var data = [
            {
                name: '张三',
                age: '20', 
               address: '北京'
            }, 
           { 
               name: '李四',
                age: '21',
               address: '上海'
            },
            {  
              name: '王五',
                age: '22', 
               address: '广州' 
           }
        ] 
       // 渲染函数 
       function render(data) {
            var $container = $('#container') 
           // 清空容器,重要!!!
            $container.html('') 
           // 拼接 table
            var $table = $('<table>')
            $table.append($('<tr><td>name</td><td>age</td><td>address</td>/tr>'))
            data.forEach(function (item) { 
               $table.append($('<tr><td>' + item.name + '</td><td>' + item.age + '</td><td>' + item.address + '</td>/tr>'))
            })
            // 渲染到页面 
           $container.append($table)
        }
        $('#btn-change').click(function () {
            data[1].age = 30
            data[2].address = '深圳'
            // re-render  再次渲染 
           render(data)
        })  
      // 页面加载完立刻执行(初次渲染)
        render(data)    
</script>
</body>
</html>

复制代码

Although the above code to complete the requirements, but, unfortunately, if I only modify some of the data, the entire table

All are required to render. For browsers, rendering DOM is a very "expensive" process. So, is there any way to modify some data when rendering I just modify the DOM it?


Use VDOM achieved only render the modified DOM

Let's first look at the use of snabbdom this library, it will use VDOM to achieve local rendering. Together feel

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Document</title></head><body>
    <div id="container"></div>
    <button id="btn-change">change</button>

    <script src="https://cdn.bootcss.com/snabbdom/0.7.0/snabbdom.js"></script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.0/snabbdom-class.js"></script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.0/snabbdom-props.js"></script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.0/snabbdom-style.js"></script> 
   <script src="https://cdn.bootcss.com/snabbdom/0.7.0/snabbdom-eventlisteners.js"></script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.0/h.js"></script>
    <script type="text/javascript">
        var snabbdom = window.snabbdom
        // 定义关键函数 patch 
       var patch = snabbdom.init([
            snabbdom_class,
            snabbdom_props,
            snabbdom_style,
            snabbdom_eventlisteners
        ])
        // 定义关键函数 h 
       var h = snabbdom.h 
       // 原始数据 
       var data = [
            {
                name: '张三',
                age: '20', 
               address: '北京'
            },  
          {
                name: '李四',
                age: '21', 
               address: '上海'
            }, 
           { 
               name: '王五',
                age: '22', 
               address: '广州'
            }
        ] 
       // 把表头也放在 data 中
        data.unshift({
            name: '姓名',
            age: '年龄',
            address: '地址'
        }) 
       var container = document.getElementById('container')
        // 渲染函数
        var vnode 
       function render(data) {
            var newVnode = h('table', {}, data.map(function (item) {
                var tds = [] 
                var i
                for (i in item) {
                   if (item.hasOwnProperty(i)) { 
                       tds.push(h('td', {}, item[i] + ''))
                   }
                } 
               return h('tr', {}, tds)
            }))
            if (vnode) {
                // 如果已经渲染了
                patch(vnode, newVnode) 
           } else { 
               // 初次渲染
                patch(container, newVnode)
            } 
           // 存储当前的 vnode 结果 
           vnode = newVnode
        }  
      // 初次渲染
        render(data)
        var btnChange = document.getElementById('btn-change')
        btnChange.addEventListener('click', function () { 
           data[1].age = 30
            data[2].address = '深圳'
            // re-render
            render(data)
        })
    </script>
</body>
</html>复制代码


The above code is realized when you modify some of the data, rendering only part of the data.

So, the core is a function of two or more code to do this we need to explore. Is a function of H , is a function of patch


The key function h and the function key patch

The key function h

Function h is a value returned by the vnode, i.e. virtual DOM node, shown in FIG.


That function can use the h vnode structure similar to the right generation.


The key function patch

So the role of the key functions of the patch, vnode sucked up into real DOM node, and then plunged into the container inside.

If the container which has generated a good vnode, then, it will be a new generation of newVnode compared with previous vnode, and then find out different nodes, then replace the old node.


Until now, it has been basically understand the meaning of the VDOM and why use VDOM, we do a simple summary

  1. If only part of the data changes, and therefore have to render the entire DOM
  2. DOM is very "expensive" operation, so we need to reduce DOM manipulation
  3. Find out the node must be updated, others may not be updated


So, the next there was a problem, how do we know which nodes need to update it? This is the role diff algorithms


diff algorithm to find the DOM needs to be updated

Because the diff algorithm itself is too complex, so only we need to understand what the core of the idea can be.

So we just need to focus on rendering time, what happened, understand core processes to the following two events.

  1. patch(container, newVnode)
  2. patch(vnode, newVnode)

In other words, we need to understand is:

  1. When rendering the initial and the real VDOM rendered DOM is then inserted into the container inside.
  2. Again when rendering, new and old vnode vnode contrast, and then how to process partial rendering.


1.patch(container, newVnode)

We want to achieve is the process:


Let's look at the process of creating simulation above, just pseudo-code, we understand the general process

function createElement(vnode) {    
var tag = vnode.tag  // 'ul'    
var attrs = vnode.attrs || {}    
var children = vnode.children || []    
if (!tag) {       
 return null  
  }    
// 创建真实的 DOM 元素    
var elem = document.createElement(tag)   
 // 属性    
var attrName    
for (attrName in attrs) {    
    if (attrs.hasOwnProperty(attrName)) { 
           // 给 elem 添加属性
           elem.setAttribute(attrName, attrs[attrName])
        }
    }
    // 子元素
    children.forEach(function (childVnode) {
        // 给 elem 添加子元素,如果还有子节点,则递归的生成子节点。
        elem.appendChild(createElement(childVnode))  // 递归
    })    // 返回真实的 DOM 元素   
 return elem
}复制代码

So, by the above simulation code, it can be very good understanding of the process will initially vdom rendered to the container.


2.patch(vnode, newVnode)

This process is to newVnode and vnode contrast, the difference between partial rendering.



Then the following pseudo-code procedure:

function updateChildren(vnode, newVnode) {
    var children = vnode.children || []
    var newChildren = newVnode.children || []
    children.forEach(function (childVnode, index) {
        var newChildVnode = newChildren[index]
        if (childVnode.tag === newChildVnode.tag) {
            // 深层次对比,递归
            updateChildren(childVnode, newChildVnode)
        } else { 
           // 替换 
           replaceNode(childVnode, newChildVnode) 
       }
    }
)}
function replaceNode(vnode, newVnode) {
    var elem = vnode.elem  // 取得旧的 真实的 DOM 节点
    var newElem = createElement(newVnode)//生成新的真实的dom节点 
   // 替换
}复制代码

Then the real replacement process, what does? Simple summary:

  • Find the corresponding real dom, calledelem
  • Judgment newVnodeand oldVnodepoints to the same object, and if so, directreturn
  • If they have a text node and not equal, then the eltext node to Vnodetext nodes.
  • If you oldVnodehave child nodes but newVnodenot, delete the elchild node
  • If oldVnodeno child nodes and newVnodethere, then Vnodelater add a child node of authenticity toelem
  • If both child nodes, perform the updateChildrenfunction that compares the child nodes, this step is very important, please refer to this article


These are just a simple understanding of the processes diff algorithm, a detailed procedure on more diff algorithm, you can refer to the article read.


Reference article

Detailed vue of diff algorithms


Reproduced in: https: //juejin.im/post/5d0228436fb9a07f0357354d

Guess you like

Origin blog.csdn.net/weixin_34128501/article/details/93180800