微網(wǎng)站的鏈接怎么做的網(wǎng)站推廣蘇州
diff算法
對于React團(tuán)隊發(fā)現(xiàn)在日常開發(fā)中對于更新組件的頻率,會比新增和刪除的頻率更高,所以在diff算法里,判斷更新的優(yōu)先級會更高。對于Vue2的diff算法使用了雙指針,React的diff算法沒有使用雙指針,是因為更新的jsx對象的newChildren為數(shù)組的形式,但是和newChildren中每個組件比較的是current fiber,對fiber的兄弟節(jié)點是通過silbing來相連的,我們通過下標(biāo)來去獲取下一個newChildren項,但是對于fiber只能通過fiber.silbing來獲取對應(yīng)的項,所以沒有使用雙指針法來進(jìn)行diff。
所以React的diff算法的整體邏輯會經(jīng)歷兩輪的遍歷。
第一輪遍歷:
會嘗試逐個的復(fù)用節(jié)點;
第二輪遍歷:
處理上一輪遍歷中沒有處理完的節(jié)點。
一、第一輪遍歷:
從前往后以此進(jìn)行遍歷,存在三種情況:
-
若新舊子節(jié)點的key和type都相同,則說明可以復(fù)用;
-
若新舊子節(jié)點的key相同,但是type不同,這個時候會根據(jù)
reactElement來生成一個全新的fiber,舊的fiber被放入到deletions數(shù)組中,回頭統(tǒng)一刪除,但是注意,此時遍歷不回停止;
-
若新舊子節(jié)點的key和type都不相同,則結(jié)束遍歷。
實例1:
前:
<div>
<div key='a'>a</div>
<div key='b'>b</div>
<div key='c'>c</div>
<div key='d'>d</div>
</div>
后:
<div>
<div key='a'>a</div>
<div key='b'>d</div>
<div key='e'>e</div>
<div key='d'>d</div>
</div>
我們發(fā)現(xiàn)div.key.a和我們發(fā)現(xiàn)div.key.b可以復(fù)用,繼續(xù)往后走
走到div.key.e,我們發(fā)現(xiàn)key不同,結(jié)束第一輪遍歷;
實例2:
<div>
<div key='a'>a</div>
<div key='b'>b</div>
<div key='c'>c</div>
<div key='d'>d</div>
</div>
更新后:
<div>
<div key='a'>a</div>
<div key='b'>b</div>
<p key='c'>c</p>
<div key='d'>d</div>
</div>
前面div.keya和div.keyb都會復(fù)用,接下來到了第3個節(jié)點,我們發(fā)現(xiàn)key是相同的,但是type不同,就會將對應(yīng)的舊的fiberNode放到一個叫deletions中數(shù)組中,回頭統(tǒng)一刪除,然后根據(jù)新的react元素創(chuàng)建一個新的FiberNode,但此時的遍歷不會結(jié)束。
接下來往后面繼續(xù)遍歷,遍歷什么時候結(jié)束?
到末尾了,也就是遍歷完了
或者是和實例1相同,發(fā)現(xiàn)key不同。
二、第二輪遍歷:
如果第一輪遍歷被提前停止了,那么意味著有新的React元素或者舊的FiberNode沒有遍歷完,此時就會采用第二輪遍歷;
第二輪遍歷會處理這么三種情況:
只剩下舊子節(jié)點:將舊的子節(jié)點放到deletions數(shù)組里面直接刪除掉(刪除的情況);
只剩下新的jsx元素:根據(jù)RecreatElement元素來創(chuàng)建新的FiberNode節(jié)點(新增的情況);
新舊節(jié)點都有剩余:
會將剩余的FiberNode節(jié)點放到一個map里面,遍歷剩余的jsx元素,然后從map中找出可以復(fù)用的fiberNode,若能找到就拿來復(fù)用(移動的情況)
若不能找到,就新增,然后若剩余的jsx元素都遍歷完了,map結(jié)構(gòu)中還有剩余的fiber節(jié)點,就將這些fiber節(jié)點添加到deletions數(shù)組中,之后做統(tǒng)一刪除。
例子:
// 之前
abcd// 之后
acdb===第一輪遍歷開始===
a(之后)vs a(之前)
key不變,可復(fù)用
此時 a 對應(yīng)的oldFiber(之前的a)在之前的數(shù)組(abcd)中索引為0
所以 lastPlacedIndex = 0;繼續(xù)第一輪遍歷...c(之后)vs b(之前)
key改變,不能復(fù)用,跳出第一輪遍歷
此時 lastPlacedIndex === 0;
===第一輪遍歷結(jié)束======第二輪遍歷開始===
newChildren === cdb,沒用完,不需要執(zhí)行刪除舊節(jié)點
oldFiber === bcd,沒用完,不需要執(zhí)行插入新節(jié)點將剩余oldFiber(bcd)保存為map// 當(dāng)前oldFiber:bcd
// 當(dāng)前newChildren:cdb繼續(xù)遍歷剩余newChildrenkey === c 在 oldFiber中存在
const oldIndex = c(之前).index;
此時 oldIndex === 2; // 之前節(jié)點為 abcd,所以c.index === 2
比較 oldIndex 與 lastPlacedIndex;如果 oldIndex >= lastPlacedIndex 代表該可復(fù)用節(jié)點不需要移動
并將 lastPlacedIndex = oldIndex;
如果 oldIndex < lastplacedIndex 該可復(fù)用節(jié)點之前插入的位置索引小于這次更新需要插入的位置索引,代表該節(jié)點需要向右移動在例子中,oldIndex 2 > lastPlacedIndex 0,
則 lastPlacedIndex = 2;
c節(jié)點位置不變繼續(xù)遍歷剩余newChildren// 當(dāng)前oldFiber:bd
// 當(dāng)前newChildren:dbkey === d 在 oldFiber中存在
const oldIndex = d(之前).index;
oldIndex 3 > lastPlacedIndex 2 // 之前節(jié)點為 abcd,所以d.index === 3
則 lastPlacedIndex = 3;
d節(jié)點位置不變繼續(xù)遍歷剩余newChildren// 當(dāng)前oldFiber:b
// 當(dāng)前newChildren:bkey === b 在 oldFiber中存在
const oldIndex = b(之前).index;
oldIndex 1 < lastPlacedIndex 3 // 之前節(jié)點為 abcd,所以b.index === 1
則 b節(jié)點需要向右移動
===第二輪遍歷結(jié)束===最終acd 3個節(jié)點都沒有移動,b節(jié)點被標(biāo)記為移動
再看個例子:
// 之前
abcd
// 之后
dabc
===第一輪遍歷開始===
d(之后)vs a(之前)
key不變,type改變,不能復(fù)用,跳出遍歷
===第一輪遍歷結(jié)束===
===第二輪遍歷開始===
newChildren === dabc,沒用完,不需要執(zhí)行刪除舊節(jié)點
oldFiber === abcd,沒用完,不需要執(zhí)行插入新節(jié)點
將剩余oldFiber(abcd)保存為map
繼續(xù)遍歷剩余newChildren
// 當(dāng)前oldFiber:abcd
// 當(dāng)前newChildren dabc
key === d 在 oldFiber中存在
const oldIndex = d(之前).index;
此時 oldIndex === 3; // 之前節(jié)點為 abcd,所以d.index === 3
比較 oldIndex 與 lastPlacedIndex;
oldIndex 3 > lastPlacedIndex 0
則 lastPlacedIndex = 3;
d節(jié)點位置不變
繼續(xù)遍歷剩余newChildren
// 當(dāng)前oldFiber:abc
// 當(dāng)前newChildren abc
key === a 在 oldFiber中存在
const oldIndex = a(之前).index; // 之前節(jié)點為 abcd,所以a.index === 0
此時 oldIndex === 0;
比較 oldIndex 與 lastPlacedIndex;
oldIndex 0 < lastPlacedIndex 3
則 a節(jié)點需要向右移動
繼續(xù)遍歷剩余newChildren
// 當(dāng)前oldFiber:bc
// 當(dāng)前newChildren bc
key === b 在 oldFiber中存在
const oldIndex = b(之前).index; // 之前節(jié)點為 abcd,所以b.index === 1
此時 oldIndex === 1;
比較 oldIndex 與 lastPlacedIndex;
oldIndex 1 < lastPlacedIndex 3
則 b節(jié)點需要向右移動
繼續(xù)遍歷剩余newChildren
// 當(dāng)前oldFiber:c
// 當(dāng)前newChildren c
key === c 在 oldFiber中存在
const oldIndex = c(之前).index; // 之前節(jié)點為 abcd,所以c.index === 2
此時 oldIndex === 2;
比較 oldIndex 與 lastPlacedIndex;
oldIndex 2 < lastPlacedIndex 3
則 c節(jié)點需要向右移動
===第二輪遍歷結(jié)束===