天天看點

Virtual DOM diff算法

首先浏覽器加載一個HTML頁面時會經過建立DOM 樹、建立樣式規則(style rules)、建構渲染樹(render tree)、布局layout 和 繪制頁面(painting)。

傳統的原生api或jQuery去操作DOM的時候,浏覽器會從建構DOM樹開始從頭到尾執行一遍流程,是以頻繁的操作DOM的代價是昂貴的。還會出現頁面卡頓,影響使用者的體驗。

虛拟DOM就是為了解決這個問題而被設計出來,加入一次操作中有10次操作的動作,虛拟DOM不會立即執行這個操作,而是将這10次更新的diff内容

虛拟DOM是在DOM的基礎上在記憶體建立了一個抽象層,對資料和狀态所做的任何改動,都會被自動且高效的同步到虛拟DOM,最後再批量同步到DOM中

render執行的結果得到的并不是真正的DOM節點,而僅僅是javascript對象,稱之為虛拟DOM

虛拟DOM具有批處理和高效的Diff算法,可以無需擔心性能問題而随時‘重新整理’整個頁面,因為虛拟DOM能保證對界面上真正變化的部分進行實際的DOM操作

【傳統DOM操作(eg:innerHtml)】:render html+重建所有DOM元素

【虛拟DOM】:render 虛拟DOM + diff算法+更新必要的DOM元素

Vue.js将DOM抽象成一個以JavaScript對象為節點的虛拟DOM樹,以VNode節點模拟真實DOM,可以對這顆抽象樹進行建立節點、删除節點以及修改節點等操作,在這過程中都不需要操作真實DOM,隻需要操作JavaScript對象,大大提升了性能。修改以後經過diff算法得出一些需要修改的最小機關,再将這些小機關的視圖進行更新。這樣做減少了很多不需要的DOM操作,大大提高了性能。

diff的過程就是調用patch函數,就像打更新檔一樣修改真實dom。

function patch (oldVnode, vnode) {
    if (sameVnode(oldVnode, vnode)) {
        patchVnode(oldVnode, vnode)
    } else {
        const oEl = oldVnode.el
        let parentEle = api.parentNode(oEl)
        createEle(vnode)
        if (parentEle !== null) {
            api.insertBefore(parentEle, vnode.el, api.nextSibling(oEl))
            api.removeChild(parentEle, oldVnode.el)
            oldVnode = null
        }
    }
    return vnode
}
           

patch

函數有兩個參數,

vnode

oldVnode

,也就是新舊兩個虛拟節點。在這之前,我們先了解完整的vnode都有什麼屬性,舉個一個簡單的例子:

// body下的 <div id="v" class="classA"><div> 對應的 oldVnode 就是

{
  el:  div  //對真實的節點的引用,本例中就是document.querySelector('#id.classA')
  tagName: 'DIV',   //節點的标簽
  sel: 'div#v.classA'  //節點的選擇器
  data: null,       // 一個存儲節點屬性的對象,對應節點的el[prop]屬性,例如onclick , style
  children: [], //存儲子節點的數組,每個子節點也是vnode結構
  text: null,    //如果是文本節點,對應文本節點的textContent,否則為null
}
           

需要注意的是,el屬性引用的是此 virtual dom對應的真實dom,

patch

vnode

參數的

el

最初是null,因為

patch

之前它還沒有對應的真實dom。

繼續閱讀