使用虛擬Dom的好處以及diff演算法

2021-10-20 18:34:37 字數 3604 閱讀 4005

首先了解瀏覽器顯示網頁經歷的5個過程

1、解析標籤,生成元素樹(dom樹)

2、解析樣式,生成樣式樹

3、生成元素與樣式的關係

4、生成元素的顯示座標

5、顯示頁面

修改真實dom

每修改乙個元素,那麼這5個過程都要重新走一次。修改10個元素就走10遍。

修改虛擬dom

虛擬dom儲存在記憶體中,對10個元素的修改是在虛擬dom中進行,修改完後,比較虛擬dom和真實dom的差異,當有差異時,再一次過去更新網頁的顯示,而不是走10遍過程。

虛擬 dom 好處

速度快,減小了頁面渲染過程的次數

首先我們先整明白 diff 演算法的本質

diff演算法的本質是找出兩個物件之間的差異,目的是盡可能復用節點。
此處說到的物件其實就對應 vue中的 virtual dom,即使用 js 物件來表示頁面中的 dom 結構。

其實仔細思考下,乙個dom節點主要包含三個部分

所以我們可以設計如下的物件結構表示乙個 dom 節點

上文說了這個結論,再看下

diff演算法的本質是找出兩個物件之間的差異,目的是盡可能復用節點。
那麼我們執行 diff (vnode,vnode2),就能知道 vnode 和 vnode2 之間的差異如下:

知道了差異部分,我們就能更新檢視了,偽**如下

再思考下,當我們改變乙個節點的時候,我們其實主要改了以下部分

自身的屬性(style 、class等等)子節點
那麼 diff 演算法可以抽象為 兩部分

function diff(vnode,newvnode)

vue之前的原始碼是採用 先 diff,得到差異,然後根據差異在去 patch 真實 dom,也就是分兩步驟

diffpatch

但是這樣效能會有損失,因為 diff 過程中會遍歷一次整棵樹,patch 的時候又會遍歷整棵樹,其實這兩次遍歷可以合併成一次,也就是 在diff的同時進行patch

所以我們把流程改為

function patchvnode(oldvnode, vnode, parentelm)

patchattr

function patchattr(oldvnode = {}, vnode = {}, parentelm)

else

})each(vnode, (key, val) => )

}function each(obj, fn)

for (var key in obj) }}

function setattr(node, key, value) )

break

case 『value』:

var tag = node.tag || 『』

tag = tag.tolowercase()

if (

tag === 『input』 || tag === 『textarea』

) else

break

default:

node.setattribute(key, value)

break}}

該函式主要做了兩件事

遍歷 oldvnode 看 newtreeattr 是否還有對應的屬性如果有並且不相等的,修改對應的屬性, 沒有的話,直接刪除對應的屬性遍歷oldvnode, 是否還有對應的屬性,沒有就新增

patchchildren

先看下原始碼

function patchchildren(parentelm, oldch, newch) else if (!oldendvnode)

else if (samevnode(oldstartvnode, newstartvnode)) 

else if (samevnode(oldendvnode, newendvnode))

else if (samevnode(oldstartvnode, newendvnode))

else if (samevnode(oldendvnode, newstartvnode))

else else else

}}

if (oldstartidx > oldendidx) else if (newstartidx > newendidx)

上述**的本質是找出兩個陣列的差異
舉個栗子

舊陣列 [a,b,c,d]

新陣列 [e,f,g,h]

怎麼找出新舊陣列之間的差異呢? 我們約定以下名詞 - 舊首(舊陣列的第乙個元素) - 舊尾(舊陣列的最後乙個元素) - 新首(新陣列的第乙個元素) - 新尾(新陣列的最後乙個元素)

一些工具函式

-samevnode--用於判斷節點是否應該復用,這裡做了一些簡化,實際的diff演算法複雜些,這裡只用tag 和 key 相同,我們就復用節點,執行patchvnode,即對節點進行修改
function samevnode(a, b)

createkeytooldidx--建立key-index的索引,主要是替代遍歷,提公升效能
function createkeytooldidx(children, beginidx, endidx)

for (i = beginidx; i <= endidx; ++i)

return map

}

舊首 和 新首 對比
if (samevnode(oldstartvnode, newstartvnode))

舊尾 和 新尾 對比
if (samevnode(oldendvnode, newendvnode))

舊首 和 新尾 對比
if (samevnode(oldstartvnode, newendvnode))

舊尾 和 新首 對比,將 舊尾 移動到 最前面
if (samevnode(oldendvnode, newstartvnode))

首尾對比 都不 符合 samevnode 的話

嘗試 用 newch 的第一項在 oldch 內尋找 samevnode,如果在 oldch 不存在對應的 samevnode ,則直接建立乙個,存在的話則判斷

符合 samevnode,則移動 oldch 對應的 節點不符合 samevnode ,建立新節點

最後 通過 oldstartidx > oldendidx ,來判斷 oldch 和 newch 哪乙個先遍歷完成

oldch 先遍歷完成,則證明 newch 還有多餘節點,需要新增這些節點newch 先遍歷完成,則證明 oldch 還有多餘節點,需要刪除這些節點

總結

diff 演算法的本質是找出兩個物件之間的差異diff 演算法的核心是子節點陣列對比,思路是通過 首尾兩端對比key 的作用 主要是

決定節點是否可以復用建立key-index的索引,主要是替代遍歷,提公升效能

redis的好處以及應用

redis的好處 a 速度比較快 因為資料是在記憶體中的,b 支援較多的資料型別 比如 string list set map 等 c 支援事物 操作都是源自性的 對於資料而言要麼全部執行 要麼全部不執行 d 豐富的特性 可用於快取 訊息 設定key的過期時間 與memcached相比較的優勢 re...

執行緒鎖的使用 和好處 以及缺點

上面寫錯了函式名 是mutex.acquire import threading from threading import thread from threading import lock number 0 lock lock def work3 global number for i in r...

解耦的好處以及哪來的這麼多好處

關於解耦合的乙個現實例子 跟大部分餐飲企業一樣,星巴克也主要致力於將訂單處理的吞吐量最大化。顧客訂單越多,收入就越多。為此,他們採取了非同步處理的辦法。你在點單時,收銀員取出乙隻咖啡杯,在上面作上記號表明你點的是什麼,然後把這個杯子放到佇列裡去。這裡的佇列指的是在咖啡機前排成一列的咖啡杯。正是這個佇...