老熟女激烈的高潮_日韩一级黄色录像_亚洲1区2区3区视频_精品少妇一区二区三区在线播放_国产欧美日产久久_午夜福利精品导航凹凸

重慶分公司,新征程啟航

為企業提供網站建設、域名注冊、服務器等服務

虛擬DOM如何實現-創新互聯

這篇文章主要介紹虛擬DOM如何實現,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!

創新互聯公司主要從事成都網站制作、成都網站建設、外貿營銷網站建設、網頁設計、企業做網站、公司建網站等業務。立足成都服務蠡縣,十年網站建設經驗,價格優惠、服務專業,歡迎來電咨詢建站服務:028-86922220

Virtual DOM中Diff算法得到的結果如何映射到真實DOM中,我們將在下一篇博客揭曉。

主要內容為:

Virtual DOM的結構Virtual DOM的Diff算法

注:這個Virtual DOM的實現并不是React Virtual DOM的源碼,而是基于virtual-dom)這個庫。兩者在原理上類似,并且這個庫更加簡單容易理解。相較于這個庫,React對Virtual DOM做了進一步的優化和調整,我會在后續的博客中進行分析。

Virtual DOM的結構

VirtualNode

作為Virtual DOM的元數據結構,VirtualNode位于vnode/vnode.js文件中。我們截取一部分聲明代碼來看下內部結構:

function VirtualNode(tagName, properties, children, key, namespace) {
    this.tagName = tagName
    this.properties = properties || noProperties //props對象,Object類型
    this.children = children || noChildren //子節點,Array類型
    this.key = key != null ? String(key) : undefined
    this.namespace = (typeof namespace === "string") ? namespace : null
    
    ...

    this.count = count + descendants
    this.hasWidgets = hasWidgets
    this.hasThunks = hasThunks
    this.hooks = hooks
    this.descendantHooks = descendantHooks
}

VirtualNode.prototype.version = version //VirtualNode版本號,isVnode()檢測標志
VirtualNode.prototype.type = "VirtualNode" // VirtualNode類型,isVnode()檢測標志

上面就是一個VirtualNode的完整結構,包含了特定的標簽名、屬性、子節點等。

VText

VText是一個純文本的節點,對應的是HTML中的純文本。因此,這個屬性也只有text這一個字段。

function VirtualText(text) {
    this.text = String(text)
}

VirtualText.prototype.version = version
VirtualText.prototype.type = "VirtualText"

VPatch

VPatch是表示需要對Virtual DOM執行的操作記錄的數據結構。它位于vnode/vpatch.js文件中。我們來看下里面的具體代碼:

// 定義了操作的常量,如Props變化,增加節點等
VirtualPatch.NONE = 0
VirtualPatch.VTEXT = 1
VirtualPatch.VNODE = 2
VirtualPatch.WIDGET = 3
VirtualPatch.PROPS = 4
VirtualPatch.ORDER = 5
VirtualPatch.INSERT = 6
VirtualPatch.REMOVE = 7
VirtualPatch.THUNK = 8

module.exports = VirtualPatch

function VirtualPatch(type, vNode, patch) {
    this.type = Number(type) //操作類型
    this.vNode = vNode //需要操作的節點
    this.patch = patch //需要操作的內容
}

VirtualPatch.prototype.version = version
VirtualPatch.prototype.type = "VirtualPatch"

其中常量定義了對VNode節點的操作。例如:VTEXT就是增加一個VText節點,PROPS就是當前節點有Props屬性改變。

Virtual DOM的Diff算法

了解了虛擬DOM中的三個結構,那我們下面來看下Virtual DOM的Diff算法。

這個Diff算法是Virtual DOM中最核心的一個算法。通過輸入初始狀態A(VNode)和最終狀態B(VNode),這個算法可以得到從A到B的變化步驟(VPatch),根據得到的這一連串步驟,我們就可以知道哪些節點需要新增,哪些節點需要刪除,哪些節點的屬性有了變化。在這個Diff算法中,又分成了三部分:

VNode的Diff算法Props的Diff算法Vnode children的Diff算法

下面,我們就來一個一個介紹這些Diff算法。

VNode的Diff算法

該算法是針對于單個VNode的比較算法。它是用于兩個樹中單個節點比較的場景。具體算法如下,如果不想直接閱讀源碼的同學也可以翻到下面,會有相關代碼流程說明供大家參考:

function walk(a, b, patch, index) {
    if (a === b) {
        return
    }

    var apply = patch[index]
    var applyClear = false

    if (isThunk(a) || isThunk(b)) {
        thunks(a, b, patch, index)
    } else if (b == null) {

        // If a is a widget we will add a remove patch for it
        // Otherwise any child widgets/hooks must be destroyed.
        // This prevents adding two remove patches for a widget.
        if (!isWidget(a)) {
            clearState(a, patch, index)
            apply = patch[index]
        }

        apply = appendPatch(apply, new VPatch(VPatch.REMOVE, a, b))
    } else if (isVNode(b)) {
        if (isVNode(a)) {
            if (a.tagName === b.tagName &&
                a.namespace === b.namespace &&
                a.key === b.key) {
                var propsPatch = diffProps(a.properties, b.properties)
                if (propsPatch) {
                    apply = appendPatch(apply,
                        new VPatch(VPatch.PROPS, a, propsPatch))
                }
                apply = diffChildren(a, b, patch, apply, index)
            } else {
                apply = appendPatch(apply, new VPatch(VPatch.VNODE, a, b))
                applyClear = true
            }
        } else {
            apply = appendPatch(apply, new VPatch(VPatch.VNODE, a, b))
            applyClear = true
        }
    } else if (isVText(b)) {
        if (!isVText(a)) {
            apply = appendPatch(apply, new VPatch(VPatch.VTEXT, a, b))
            applyClear = true
        } else if (a.text !== b.text) {
            apply = appendPatch(apply, new VPatch(VPatch.VTEXT, a, b))
        }
    } else if (isWidget(b)) {
        if (!isWidget(a)) {
            applyClear = true
        }

        apply = appendPatch(apply, new VPatch(VPatch.WIDGET, a, b))
    }

    if (apply) {
        patch[index] = apply
    }

    if (applyClear) {
        clearState(a, patch, index)
    }
}

代碼具體邏輯如下:

如果a和b這兩個VNode全等,則認為沒有修改,直接返回。

如果其中有一個是thunk,則使用thunk的比較方法thunks。

如果a是widget且b為空,那么通過遞歸將a和它的子節點的remove操作添加到patch中。

如果b是VNode的話,

如果a也是VNode,那么比較tagName、namespace、key,如果相同則比較兩個VNode的Props(用下面提到的diffProps算法),同時比較兩個VNode的children(用下面提到的diffChildren算法);如果不同則直接將b節點的insert操作添加到patch中,同時將標記位置為true。

如果a不是VNode,那么直接將b節點的insert操作添加到patch中,同時將標記位置為true。

如果b是VText的話,看a的類型是否為VText,如果不是,則將VText操作添加到patch中,并且將標志位設置為true;如果是且文本內容不同,則將VText操作添加到patch中。

如果b是Widget的話,看a的類型是否為widget,如果是,將標志位設置為true。不論a類型為什么,都將Widget操作添加到patch中。

檢查標志位,如果標識為為true,那么通過遞歸將a和它的子節點的remove操作添加到patch中。

這就是單個VNode節點的diff算法全過程。這個算法是整個diff算法的入口,兩棵樹的比較就是從這個算法開始的。

Prpps的Diff算法

看完了單個VNode節點的diff算法,我們來看下上面提到的diffProps算法。

該算法是針對于兩個比較的VNode節點的Props比較算法。它是用于兩個場景中key值和標簽名都相同的情況。具體算法如下,如果不想直接閱讀源碼的同學也可以翻到下面,會有相關代碼流程說明供大家參考:

function diffProps(a, b) {
    var diff

    for (var aKey in a) {
        if (!(aKey in b)) {
            diff = diff || {}
            diff[aKey] = undefined
        }

        var aValue = a[aKey]
        var bValue = b[aKey]

        if (aValue === bValue) {
            continue
        } else if (isObject(aValue) && isObject(bValue)) {
            if (getPrototype(bValue) !== getPrototype(aValue)) {
                diff = diff || {}
                diff[aKey] = bValue
            } else if (isHook(bValue)) {
                 diff = diff || {}
                 diff[aKey] = bValue
            } else {
                var objectDiff = diffProps(aValue, bValue)
                if (objectDiff) {
                    diff = diff || {}
                    diff[aKey] = objectDiff
                }
            }
        } else {
            diff = diff || {}
            diff[aKey] = bValue
        }
    }

    for (var bKey in b) {
        if (!(bKey in a)) {
            diff = diff || {}
            diff[bKey] = b[bKey]
        }
    }

    return diff
}

代碼具體邏輯如下:

  1. 遍歷a對象。

    1. 當key值不存在于b,則將此值存儲下來,value賦值為undefined
    2. 當此key對應的兩個屬性都相同時,繼續終止此次循環,進行下次循環。
    3. 當key值對應的value不同且key值對應的兩個value都是對象時,判斷Prototype值,如果不同則記錄key對應的b對象的值;如果b對應的value是hook的話,記錄b的值。
    4. 上面條件判斷都不同且都是對象時,則繼續比較key值對應的兩個對象(遞歸)。
    5. 當有一個不是對象時,直接將b對應的value進行記錄。
  2. 遍歷b對象,將所有a對象中不存在的key值對應的對象都記錄下來。

整個算法的大致流程如下,因為比較簡單,就不畫相關流程圖了。如果邏輯有些繞的話,可以配合代碼食用,效果更佳。

Vnode children的Diff算法

下面讓我們來看下最后一個算法,就是關于兩個VNode節點的children屬性的diffChildren算法。這個個diff算法分為兩個部分,第一部分是將變化后的結果b的children進行順序調整的算法,保證能夠快速的和a的children進行比較;第二部分就是將a的children與重新排序調整后的b的children進行比較,得到相關的patch。下面,讓我們一個一個算法來看。

reorder算法

該算法的作用是將b節點的children數組進行調整重新排序,讓ab兩個children之間的diff算法更加節約時間。具體代碼如下:

function reorder(aChildren, bChildren) {
    // O(M) time, O(M) memory
    var bChildIndex = keyIndex(bChildren)
    var bKeys = bChildIndex.keys  // have "key" prop,object
    var bFree = bChildIndex.free  //don't have "key" prop,array

    // all children of b don't have "key"
    if (bFree.length === bChildren.length) {
        return {
            children: bChildren,
            moves: null
        }
    }

    // O(N) time, O(N) memory
    var aChildIndex = keyIndex(aChildren)
    var aKeys = aChildIndex.keys
    var aFree = aChildIndex.free

    // all children of a don't have "key"
    if (aFree.length === aChildren.length) {
        return {
            children: bChildren,
            moves: null
        }
    }

    // O(MAX(N, M)) memory
    var newChildren = []

    var freeIndex = 0
    var freeCount = bFree.length
    var deletedItems = 0

    // Iterate through a and match a node in b
    // O(N) time,
    for (var i = 0 ; i < aChildren.length; i++) {
        var aItem = aChildren[i]
        var itemIndex

        if (aItem.key) {
            if (bKeys.hasOwnProperty(aItem.key)) {
                // Match up the old keys
                itemIndex = bKeys[aItem.key]
                newChildren.push(bChildren[itemIndex])

            } else {
                // Remove old keyed items
                itemIndex = i - deletedItems++
                newChildren.push(null)
            }
        } else {
            // Match the item in a with the next free item in b
            if (freeIndex < freeCount) {
                itemIndex = bFree[freeIndex++]
                newChildren.push(bChildren[itemIndex])
            } else {
                // There are no free items in b to match with
                // the free items in a, so the extra free nodes
                // are deleted.
                itemIndex = i - deletedItems++
                newChildren.push(null)
            }
        }
    }

    var lastFreeIndex = freeIndex >= bFree.length ?
        bChildren.length :
        bFree[freeIndex]

    // Iterate through b and append any new keys
    // O(M) time
    for (var j = 0; j < bChildren.length; j++) {
        var newItem = bChildren[j]

        if (newItem.key) {
            if (!aKeys.hasOwnProperty(newItem.key)) {
                // Add any new keyed items
                // We are adding new items to the end and then sorting them
                // in place. In future we should insert new items in place.
                newChildren.push(newItem)
            }
        } else if (j >= lastFreeIndex) {
            // Add any leftover non-keyed items
            newChildren.push(newItem)
        }
    }

    var simulate = newChildren.slice()
    var simulateIndex = 0
    var removes = []
    var inserts = []
    var simulateItem

    for (var k = 0; k < bChildren.length;) {
        var wantedItem = bChildren[k]
        simulateItem = simulate[simulateIndex]

        // remove items
       while (simulateItem === null && simulate.length) {
            removes.push(remove(simulate, simulateIndex, null))
            simulateItem = simulate[simulateIndex]
        }

        if (!simulateItem || simulateItem.key !== wantedItem.key) {
            // if we need a key in this position...
            if (wantedItem.key) {
                if (simulateItem && simulateItem.key) {
                    // if an insert doesn't put this key in place, it needs to move
                    if (bKeys[simulateItem.key] !== k + 1) {
                        removes.push(remove(simulate, simulateIndex, simulateItem.key))
                        simulateItem = simulate[simulateIndex]
                        // if the remove didn't put the wanted item in place, we need to insert it
                        if (!simulateItem || simulateItem.key !== wantedItem.key) {
                            inserts.push({key: wantedItem.key, to: k})
                        }
                        // items are matching, so skip ahead
                        else {
                            simulateIndex++
                        }
                    }
                    else {
                        inserts.push({key: wantedItem.key, to: k})
                    }
                }
                else {
                    inserts.push({key: wantedItem.key, to: k})
                }
                k++
            }
            // a key in simulate has no matching wanted key, remove it
            else if (simulateItem && simulateItem.key) {
                removes.push(remove(simulate, simulateIndex, simulateItem.key))
            }
        }
        else {
            simulateIndex++
            k++
        }
    }

    // remove all the remaining nodes from simulate
   while(simulateIndex < simulate.length) {
        simulateItem = simulate[simulateIndex]
        removes.push(remove(simulate, simulateIndex, simulateItem && simulateItem.key))
    }

    // If the only moves we have are deletes then we can just
    // let the delete patch remove these items.
    if (removes.length === deletedItems && !inserts.length) {
        return {
            children: newChildren,
            moves: null
        }
    }

    return {
        children: newChildren,
        moves: {
            removes: removes,
            inserts: inserts
        }
    }
}

下面,我們來簡單介紹下這個排序算法:

  1. 檢查ab中的children是否擁有key字段,如果沒有,直接返回b的children數組。
  2. 如果存在,初始化一個數組newChildren,遍歷a的children元素。

    1. 如果aChildren存在key值,則去bChildren中找對應key值,如果bChildren存在則放入新數組中,不存在則放入一個null值。
    2. 如果aChildren不存在key值,則從bChildren中不存在key值的第一個元素開始取,放入新數組中。
  3. 遍歷bChildren,將所有achildren中沒有的key值對應的value或者沒有key,并且沒有放入新數組的子節點放入新數組中。
  4. 將bChildren和新數組逐個比較,得到從新數組轉換到bChildren數組的move操作patch(即remove+insert)。
  5. 返回新數組和move操作列表。

通過上面這個排序算法,我們可以得到一個新的b的children數組。在使用這個數組來進行比較厚,我們可以將兩個children數組之間比較的時間復雜度從o(n^2)轉換成o(n)。具體的方法和效果我們可以看下面的DiffChildren算法。

DiffChildren算法

function diffChildren(a, b, patch, apply, index) {
    var aChildren = a.children
    var orderedSet = reorder(aChildren, b.children)
    var bChildren = orderedSet.children

    var aLen = aChildren.length
    var bLen = bChildren.length
    var len = aLen > bLen ? aLen : bLen

    for (var i = 0; i < len; i++) {
        var leftNode = aChildren[i]
        var rightNode = bChildren[i]
        index += 1

        if (!leftNode) {
            if (rightNode) {
                // Excess nodes in b need to be added
                apply = appendPatch(apply,
                    new VPatch(VPatch.INSERT, null, rightNode))
            }
        } else {
           walk(leftNode, rightNode, patch, index)
        }

        if (isVNode(leftNode) && leftNode.count) {
            index += leftNode.count
        }
    }

    if (orderedSet.moves) {
        // Reorder nodes last
        apply = appendPatch(apply, new VPatch(
            VPatch.ORDER,
            a,
            orderedSet.moves
        ))
    }

    return apply
}

通過上面的重新排序算法整理了以后,兩個children比較就只需在相同下標的情況下比較了,即aChildren的第N個元素和bChildren的第N個元素進行比較。然后較長的那個元素做insert操作(bChildren)或者remove操作(aChildren)即可。最后,我們將move操作再增加到patch中,就能夠抵消我們在reorder時對整個數組的操作。這樣只需要一次便利就得到了最終的patch值。

以上是虛擬DOM如何實現的所有內容,感謝各位的閱讀!希望分享的內容對大家有幫助,更多相關知識,歡迎關注創新互聯行業資訊頻道!

另外有需要云服務器可以了解下創新互聯scvps.cn,海內外云服務器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務器、裸金屬服務器、高防服務器、香港服務器、美國服務器、虛擬主機、免備案服務器”等云主機租用服務以及企業上云的綜合解決方案,具有“安全穩定、簡單易用、服務可用性高、性價比高”等特點與優勢,專為企業上云打造定制,能夠滿足用戶豐富、多元化的應用場景需求。


網站欄目:虛擬DOM如何實現-創新互聯
網頁網址:http://www.xueling.net.cn/article/pdjse.html

其他資訊

在線咨詢
服務熱線
服務熱線:028-86922220
TOP
主站蜘蛛池模板: 午夜一区一品日本 | 国产麻豆一精品一aV一免费软件 | 在线一区不卡 | 懂色av影视一区二区三区 | 成年人免费公开视频 | 久久躁躁天天添久久久 | 欧美最猛性xxxxx免费 | 乱中年女人伦av三区 | 91色噜噜狠狠狠狠色综合 | 亚洲欧美日韩国产专区一区 | 欧美极品一区二区 | 国产午夜福利伦理300 | 国产51人人成人人人人爽色哟哟 | 人间精品视频在线播放 | 国产精品视频色 | 国产在线观看高清 | 国产大胸A在线观看 | 色女人天堂 | 欧美男男激情videosgay | 亚洲成人网站在线观看 | 综合久久2019 | 欧美日本不卡视频 | 人人看91视频 | 阿娇囗交全套码在线观看 | 免费一区二区视频 | 国产精品无码首页自拍 | 欧美成年免费a级 | 久久久九九九热 | 国产97超碰 | 久久国产精品久久精品国产 | 97国产精品自拍 | 久久99热狠狠色精品一区 | 亚洲网址| 欧美性猛交xxxx乱大交hd | 免费视频wwwxx | 天天av天天翘天天综合网 | 国产精品久久嫩一区二区免费 | 久在线观看福利视频69 | 成人性免费视频 | 国产精品| 日韩a级毛片免费观看久久 国产一级网站视频在线 |