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