虛擬DOM和Diff算法

 虛擬DOM

虛擬DOM其實就是在真實DOM之前加了一層JS對象生成的DOM

  1. 用JS對象模擬DOM
  2. 把這個虛擬DOM對象轉爲真實的DOM插入到頁面中
  3. 如果有事件修改了虛擬DOM,比較兩個虛擬DOM樹的差異,得到差異對象(補丁)
  4. 把差異對象應用到正則的DOM樹上

 diff算法

  • Diff 比較兩個虛擬DOM的區別(比較兩個對象的區別)
  • 根據兩個虛擬對象的區別,創建出補丁(patch),描述改變的內容,將這個補丁用來更新頁面
  • 差異計算: 先序深度優先遍歷

diff算法的三種優化策略

  1. 比較同級的節點(同一父節點的子節點)
    -  當發現節點已經不存在,則該節點及其子節點會被完全刪除掉,不會用於進一步的比較。這樣只需要對樹進行一次遍歷,便能完成整個DOM樹的比較。對於不同層的節點,只有簡單的創建和刪除。
  2. 相同類型的節點比較:對屬性進行重設實現節點的轉換
renderA: <div style={{color: 'red'}} />
renderB: <div style={{fontWeight: 'bold'}} />
=> [removeStyle color], [addStyle font-weight 'bold']

 

 3. 列表節點的比較(key存在的意義)

  • 添加、刪除、排序
  • 順序的調整類似插入/刪除    

模擬一個虛擬DOM

// 節點類
    class Element{
        constructor(type,props,children){
            this.type= type;
            this.props= props;
            this.children= children;
        }
    }
    // 生成節點類實例
    function createElement(type,props,children){
        return new Element(type,props,children)
    }

    // 創建一個虛擬DOM對象
    let virtualDOM= createElement('ul',{class: 'list'},[
        createElement('li',{class: 'item'},['a']),
        createElement('li',{class: 'item'},['b']),
        createElement('li',{class: 'item'},['c'])        
    ])
    console.log(virtualDOM)

    // 設置屬性的方法
    function setAttr(node,key,value){
        switch(key){
            case 'value': // node是input或者textarea
                if(node.tagName.toUpperCase()=== 'INPUT' || node.tagName.toUpperCase()=== 'TEXTAREA'){
                    node.value= value
                }else{
                    node.setAttribute(key,value)
                }
                break;
            case 'style': 
                node.style.cssText= value;
                break;
            default: 
                node.setAttribute(key,value)        
                break;
        }
    }

    // render方法將vnode 轉化成真實的dom
    function render(eleObj){
        let el= document.createElement(eleObj.type);    //生成父節點
        // 遍歷父節點的props添加屬性
        for(let key in eleObj.props){
            // 設置屬性的方法
            setAttr(el,key,eleObj.props[key])
        }

        // 遍歷父節點的children
        eleObj.children.forEach(item => {
            item= (item instanceof Element)? render(item) : document.createTextNode(item)    //創建文本節點
            el.appendChild(item)
        });
        return el
    }

    let el= render(virtualDOM)
    console.log(el)

    // 將元素插入到頁面
    function renderDOM(el,target){
        target.appendChild(el)
    }
    renderDOM(el,document.body)

模擬Diff算法

未完待續。。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章