什麼是虛擬DOM

PsChina發表於2018-12-03

虛擬 dom (virtual DOM)

這篇文件用於書寫我對虛擬 dom 的一些自己的見解。

什麼是虛擬 dom

虛擬 dom 是相對於瀏覽器所渲染出來的真實 dom 的,在react,vue等技術出現之前,我們要改變頁面展示的內容只能通過遍歷查詢 dom 樹的方式找到需要修改的 dom 然後修改樣式行為或者結構,來達到更新 ui 的目的。

這種方式相當消耗計算資源,因為每次查詢 dom 幾乎都需要遍歷整顆 dom 樹,如果建立一個與 dom 樹對應的虛擬 dom 物件( js 物件),以物件巢狀的方式來表示 dom 樹,那麼每次 dom 的更改就變成了 js 物件的屬性的更改,這樣一來就能查詢 js 物件的屬性變化要比查詢 dom 樹的效能開銷小。

為什麼操作 dom 效能開銷大

其實並不是查詢 dom 樹效能開銷大而是 dom 樹的實現模組和 js 模組是分開的這些跨模組的通訊增加了成本,以及 dom 操作引起的瀏覽器的迴流和重繪,使得效能開銷巨大,原本在 pc 端是沒有效能問題的,因為 pc 的計算能力強,但是隨著移動端的發展,越來越多的網頁在智慧手機上執行,而手機的效能參差不齊,會有效能問題。

新技術如何解決效能問題

angular,react,vue 等框架的出現就是為了解決這個問題的。

他們的思想是每次更新 dom 都儘量避免重新整理整個頁面,而是有針對性的去重新整理那被更改的一部分,來釋放掉被無效渲染佔用的 gpu,cup 效能。

angular

angular 採用的機制是 髒值檢測查機制 所有使用了 ng 指令的 scope data 和 {{}} 語法的 scope data 都會被加入髒檢測的佇列

vue

vue 採用的是虛擬 dom 通過重寫 setter , getter 實現觀察者監聽 data 屬性的變化生成新的虛擬 dom 通過 h 函式建立真實 dom 替換掉dom樹上對應的舊 dom。

react

react 也是通過虛擬 dom 和 setState 更改 data 生成新的虛擬 dom 以及 diff 演算法來計算和生成需要替換的 dom 做到區域性更新的。

一個虛擬 dom 例子

這個虛擬 dom 是我自己實現的不是 vue 或者 react 的內部實現。

const HelloWorld = {
    nodeName:'div',
    attrs:{
        className:'',
    },
    css:{
        width: '100px',
        height: '40px',
        color: 'green'
    },
    events:{
        onclick:()=>{ console.log('Hello virtual DOM') }
    },
    childrens:[
        {
            nodeName:'text',
            attrs:{
                innerText:'HelloWorld',
            },
        }
    ]
}
複製程式碼

下面我們來嘗試實現一個解析虛擬 dom 的 render 函式。

如何解析這個虛擬 dom

function render(vNode) {
    // 建立dom
    const dom = document.createElement(vNode.nodeName)
    const { attrs, css, events, childrens } = vNode

    // 新增屬性
    for(const attrName in attrs){
        dom[attrName] = attrs[attrName]
    }

    // 新增行內樣式
    for(const attrName in css){
        dom.style[attrName] = css[attrName]
    }

    // 新增事件
    for(const eventName in events){
        dom[eventName] = events[eventName]
    }

    if(childrens){
        for(const children of childrens) {
            // 生成子節點
            const childrenNode = render(children)
            // 繫結子節點
            dom.append(childrenNode)
        }
    }

    return dom
}
複製程式碼

大家可以將這段程式碼複製到瀏覽器測試一下。

const dom = render(HelloWorld)
document.body.append(dom)
複製程式碼

相關文章