溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

怎么在react中實(shí)現(xiàn)一個(gè)虛擬dom和diff算法

發(fā)布時(shí)間:2021-04-19 16:36:15 來(lái)源:億速云 閱讀:174 作者:Leah 欄目:開(kāi)發(fā)技術(shù)

這篇文章將為大家詳細(xì)講解有關(guān)怎么在react中實(shí)現(xiàn)一個(gè)虛擬dom和diff算法,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個(gè)參考,希望大家閱讀完這篇文章后對(duì)相關(guān)知識(shí)有一定的了解。

虛擬DOM,見(jiàn)名知意,就是假的DOM,我們真實(shí)的DOM掛載在頁(yè)面上的,而我們的虛擬DOM則是在內(nèi)存中的。這個(gè)就需要我們把真實(shí)的DOM抽象成一個(gè)對(duì)象放在內(nèi)存中。這個(gè)對(duì)象就可以是如下類(lèi)型:

var element = {
      tagName: 'div',
      props: {
        class: 'box'
      },
      children: {
        {
          tagName: 'p',
          props: {
            class: 'p1'
          },
          children: ['我是p1']
        }, 
         {
          tagName: 'p',
          props: {
            class: 'p2'
          },
          children: ['我是p2']
        }, 
        {
          tagName: 'p',
          props: {
            class: 'p3'
          },
          children: ['我是p3']
        },
      }
    }

我們想要構(gòu)造出這樣的對(duì)象可以自己封裝一個(gè)構(gòu)造函數(shù)如下:

function Element(tagName, props, children) {
    this.tagName = tagName
    this.props = props
    this.children = children
}

有了這個(gè)對(duì)象,我們需要把這個(gè)虛擬DOM渲染到真實(shí)DOM上,可以寫(xiě)出如下方法:

Element.prototype.render = function () {
    const { tagName, props, children } = this
    var el = document.createElement(tagName)
    for (key in props) {
        el.setAttribute(key, props[key])
    }
    children.forEach((item) => {
        const childEl = (item instanceof Element) ?
              item.render() :
        document.createTextNode(item)
        el.appendChild(childEl)
    })
    return el
}

最后我們可以new出這個(gè)對(duì)象調(diào)用render()方法然后appendChild到body中就好了:

let virtualDom = new Element('div', { class: 'box' }, [
    new Element('p', { class: 'p1' }, ['我是p1']),
    new Element('p', { class: 'p2' }, ['我是p2']),
    new Element('p', { class: 'p3' }, ['我是p3']),
])

let a = virtualDom.render()
document.body.appendChild(a)

diff算法

首先我們先了解一下diff算法的作用

如果我們的虛擬dom發(fā)生了變化,我們的內(nèi)存中又會(huì)產(chǎn)生新的虛擬DOM,如果我們直接用這個(gè)新的虛擬DOM結(jié)構(gòu)的話(huà),又會(huì)導(dǎo)致很多重復(fù)的渲染,因此 這個(gè)時(shí)候diff算法的作用就體現(xiàn)了出來(lái),diff通過(guò)比較新舊兩個(gè)虛擬DOM樹(shù),找出差異,并且記錄下來(lái),然后把記錄的差異應(yīng)用到真實(shí)的DOM樹(shù)上。

原理:

diff算法通過(guò)對(duì)新舊兩顆樹(shù)進(jìn)行深度優(yōu)先遍歷,每一個(gè)節(jié)點(diǎn)都加一個(gè)唯一的標(biāo)識(shí)。

這個(gè)過(guò)程分為2步

  • 找出兩個(gè)樹(shù)的差異,并記錄在一個(gè)偽數(shù)組里。

  • 把這些不同應(yīng)用到真實(shí)的DOM樹(shù)上

對(duì)于dom的操作基本可化為4種類(lèi)型

  • 對(duì)節(jié)點(diǎn)的刪除,移動(dòng),添加子節(jié)點(diǎn)

  • 更換節(jié)點(diǎn)標(biāo)簽

  • 對(duì)于文本節(jié)點(diǎn),修改節(jié)點(diǎn)文本

  • 修改節(jié)點(diǎn)props

下面會(huì)用偽代碼的形式大致過(guò)一下這個(gè)流程

// diff 函數(shù),對(duì)比兩棵樹(shù)
function diff(oldTree, newTree) {
    var patchs = {};  // 偽數(shù)組,記錄差異
    //  對(duì)4種節(jié)點(diǎn)做錯(cuò)判斷
    dfWork(oldTree, newTree, patchs, index)

    return patchs

}
function dfWork(oldTree, newTree, patchs, index) {
    let currentPatch = []

    if (1) { // 對(duì)節(jié)點(diǎn)的刪除
        currentPatch.push()
    } else if (3) { // 對(duì)節(jié)點(diǎn)的文本的更換
        currentPatch.push()

    } else { // 修改節(jié)點(diǎn)的props 對(duì)children的檢查
        // 對(duì)props作diff算法,把變化記錄到patchs中。
        currentPatch.push({ type: patch.PROPS, props: propsPatches })
        // 然后需要對(duì)子節(jié)點(diǎn)作diff算法
        diffChildren(oldNode.children, newNode.children, index, patches, currentPatch)
    }
}
function diffChildren(oldChildren, newChildren, index, patches, currentPatch) {

    // 對(duì)子節(jié)點(diǎn)作diff算法,遍歷子節(jié)點(diǎn),遞歸調(diào)用dfWork,做差異得到patchs

}
// 把變化應(yīng)用在真實(shí)的DOM樹(shù)上
function patch(node, patchs) {
    // node為老的DOM樹(shù),patchs變化。
    // 我們會(huì)遍歷這個(gè)patchs,并且把node和patch對(duì)應(yīng)上,
}
function applyPatch(node, patchs) {
    // 應(yīng)為每個(gè)節(jié)點(diǎn)可能有多個(gè)變化,所以也需要遍歷
    switch (patchs.type) {
        case REPLACE: // 節(jié)點(diǎn)替換
            // node.render() 
            break;
        case REORDER:  // 節(jié)點(diǎn)的移動(dòng)刪除新增子節(jié)點(diǎn)。

            break;
        case PROPS:
            // setProps
            break;
        case TEXT: // 對(duì)節(jié)點(diǎn)文本的修改
            // node.nodeValue
            break;

        default:
            break;
    }
}

關(guān)于怎么在react中實(shí)現(xiàn)一個(gè)虛擬dom和diff算法就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI