虛擬Dom

Virtual Dom

  • vdom 是vue和react的核心
  • vdom是什麼東西,有什麼用,爲何會存在vdom?
  • vdom如何應用,核心API是什麼?
  • diff算法

## 什麼是vdom ##javascript

  • 用js模擬DOM結構
  • DOM變化的對比,放在JS層來作
  • 提升重繪性能
<ul id="list">
   <li class="item">Item 1</li>
   <li class="item">Item 2</li>
</ul>

用js來模擬css

{
   tag:"ul",
   attrs:{
     id:"list"
   },
   children:[
    {
       tag:"li",
       attrs:{ className: "item"},  //class是js的保留字,因此用className
       children:['Item 1']  
    },{
       tag:"li",
       attrs:{ className: "item"},
       children:['Item 2']  
      }
    ]
 }

設計一個需求場景,渲染一個數組成表格

//Jquery的實現
 <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>Document</title>
        <script type="text/javascript" src="https://cdn.bootcss.com/jquery/3.4.0/jquery.min.js"></script>
        <script type="text/javascript">
           var dataList = [
               {
                   name:'111',
                   age:1
               },{
                   name:'222',
                   age:2
               },{
                   name:'333',
                   age:3
               },{
                   name:'444',
                   age:4
               },
           ]
           $(document).ready(function () {
            function render(data){
               var $container = $('#container')
               $container.html('')
               //拼接tabel
               $table = $('<table>')
               $table.append($('<tr><td>name</td><td>age</td></tr>'))
               //渲染到頁面
               data.forEach(item => {
                $table.append($(`<tr><td>${item.name}</td><td>${item.age}</td></tr>`))
               });
               $container.append($table)
           }  
            render(dataList)
            $("#btn-change").click(function(){
               dataList[1].age=30  //每次修改數據都會清空dom,而後重繪表格
               render(dataList)
            })
           })
        
        </script>
    </head>
    <body>
        <div id="container"></div>
        <button id="btn-change">修改數據</button>
    </body>
    </html>

上述辦法遇到的問題html

    • js原生或者是Jquery框架時代,都是直接操做DOM節點來進行渲染頁面,但是這樣的代價確實是很大,須要將本來的DOM所有清除,而後在從新渲染一遍
    • 操做Dom很是昂貴。每一個Dom自帶了太多的屬性。 js運行效率高
    • 儘可能減小Dom操做
    • 項目越複雜,運行效率越低,影響越嚴重
    • vdom 能夠解決這個問題,將Dom操做方在Js層,提升效率

    vdom如何應用,核心API

    • snabbdom
      爲何是snabbdom.js
      因爲虛擬dom有那麼多的好處並且現代前端框架中react和vue均不一樣程度的使用了虛擬dom的技術,所以經過一個簡單的 庫賴學習虛擬dom技術就十分必要了,至於爲何會選擇snabbdom.js這個庫呢?緣由主要有兩個:前端

      源碼簡短,整體代碼行數不超過500行。
      著名的vue的虛擬dom實現也是參考了snabbdom.js的實現。vue

      • 用snabbdomjs 實現上述例子
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>Document</title>
        <script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom.js"></script>
        <script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-class.js"></script>
        <script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-props.js"></script>
        <script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-style.js"></script>
        <script src="https://cdn.bootcss.com/snabbdom/0.7.1/snabbdom-eventlisteners.js"></script>
        <script src="https://cdn.bootcss.com/snabbdom/0.7.1/h.min.js"></script>
    
    </head>
    <body>
        <div id="container"></div>
        <button id="btn-change">修改數據</button>
      
        <script type="text/javascript">
            var snabbdom = window.snabbdom
     
            //定義patch
            var patch = snabbdom.init([
               snabbdom_class,
               snabbdom_props,
               snabbdom_style,
               snabbdom_eventlisteners
            ])
     
            var h = snabbdom.h
            var container = document.getElementById("container")
            //生成vnode
            var vnode= h('ul#list',{},[
                h('li.item',{},'Item 1'),
                h('li.item',{},'Item 2')
            ])
     
            patch(container,vnode)
     
            document.getElementById("btn-change").addEventListener('click',function(){
                console.log("111")
                var newVnode =  h('ul#list',{},[
                h('li.item',{},'Item 1'),
                h('li.item',{},'Item B'),
                h('li.item',{},'Item 3')
                ])
    
               patch(vnode,newVnode)
            })
         </script>
    </body>
    </html>

    //修改數據只是修改了 第二個item 第三,第一個數據沒變化(F12查看Element 第一個item沒有閃爍)java

    diff算法

    • 什麼是diff算法
    • 去繁就簡
    • vdom 爲什麼用diff算法
    • diff算法的實現流程

    diff命令是linux系統自帶的基礎命令
    git diff 判斷文本文件哪裏被修改了
    diff算法一直都在,並非由於react、vue纔出現的node

    vdom爲什麼使用diff算法

    • DOM 操做是昂貴的,所以儘可能減小DOM操做
    • 找出本次DOM必須更新的節點來更新,其餘的不更新
    • 這個找出的過程,就須要diff算法

    diff實現過程

    只須要明白react

    • path(container,vnode)
    • path(vnode,newnode)

    經過VNode建立一個真實的DOM的流程jquery

    function createElement(vnode){
       var tag= vnode.tag
       var attrs = vnode.attrs||{}
       var children = vnode.children || []
       if(!tag){
           return null
       }
       var elem = document.createElement(tag)
        var attrName 
        for(attrName in attrs){
         if(attrs.hasOwnProperty(attrName)){
            elem.setAttribute(attrName,attrs[attrName])
          }
        }
    
        children.forEach(childNode => {
            elem.appendChild(createElement(childNode))
        });
        //返回真實的Dom
        return elem
    }

    path(vnode,newVnode) 的實現,linux

    function updateChildren(vnode,newVnode){
         var children = vnode.children || []
         var newChildren = newVnode.children || []
        
         //遍歷現有的children
         children.forEach((child,index )=> {
             var newChild = newChildren[index]
             if(newChild == null){
                return
             }
    
             if(child.tag === newChild.tag){
                  updateChildren(child,newChild)
             }else{
                 replaceNode(child,newChild)
             }
         });
    }
    
    function replaceNode(vnode,newVnode){
        var elem = vnode.elem
        var newElem = createElement(newVnode)
    }

    不單單是以上的內容,還有如下的內容

    • 節點新增和刪除
    • 節點從新排序
    • 節點屬性、樣式、事件綁定
    相關文章
    相關標籤/搜索