寫這篇博客的初衷是為了加深自己對該知識點(diǎn)的理解,同時(shí)也是記錄一下自己的遇到的問題厦坛,避免自己以后再犯相同的錯(cuò)誤帖努。為了解釋清除這個(gè)問題,接下來我們先大概了解一下 react 中的 diff 算法粪般。
diff 的分類
tree diff(針對樹結(jié)構(gòu))
- react 對兩棵樹進(jìn)行層次比較拼余,對第一層的比較結(jié)束,則接著比較下一層次亩歹,
- 若某一節(jié)點(diǎn)消失了匙监,則該節(jié)點(diǎn)與其所有子節(jié)點(diǎn)會被完全刪除,不在進(jìn)行進(jìn)一步比較。
component diff(針對組件結(jié)構(gòu))
- 首先判斷兩個(gè)組件是不是同一類型小作,若是同一類型亭姥,則繼續(xù)比較 Dom 樹,如果不是顾稀,則替換該組件下的所有子節(jié)點(diǎn)达罗。
- 如果我們確切的知道組件是同一類型,并且它的虛擬dom沒有變化静秆。我們可以通過 shouldComponentUpdate() 來判斷該組件是否需要進(jìn)行 diff粮揉。從而節(jié)省大量的 diff 運(yùn)算時(shí)間。
element diff(針對元素結(jié)構(gòu))
該 diff 算法主要是針對同一層級的節(jié)點(diǎn)抚笔,react 對處于同一層級的節(jié)點(diǎn)提供了以下三種操作
- 插入節(jié)點(diǎn): 新的節(jié)點(diǎn)不在原來該層級的 dom 節(jié)點(diǎn)集合中扶认,則執(zhí)行插入操作。
- 移動(dòng)節(jié)點(diǎn):若某一節(jié)點(diǎn)在該層級原本 dom 集合中存在殊橙,并且新的dom集合中也存在辐宾,react 判斷他們?yōu)橥辉兀皇俏恢貌煌蚵瑒t僅僅移動(dòng)該節(jié)點(diǎn)叠纹。而 key 則是 react 判斷是否為同一元素的依據(jù)。
- 刪除節(jié)點(diǎn):若該節(jié)點(diǎn)在該層級原本的 dom 集合存在敞葛,但新的 dom 集合中不存在誉察,則刪除該節(jié)點(diǎn),或者該層級原本的 dom 集合存在制肮,新的 dom 集合也存在冒窍,但react判斷他們并不是同一元素,也需要在原本的集合中刪除該節(jié)點(diǎn)豺鼻。
問題起因
某次頁面開發(fā)涉及到了節(jié)點(diǎn)的移動(dòng)综液,但我往其中插入某個(gè)新的節(jié)點(diǎn)的時(shí)候,發(fā)現(xiàn)整個(gè)頁面的數(shù)據(jù)出現(xiàn)了問題儒飒,經(jīng)過調(diào)試谬莹,發(fā)現(xiàn)新建的節(jié)點(diǎn)雖然在頁面中的位置是正確的,但它的 props 的 id 號確是頁面更新前這個(gè)節(jié)點(diǎn)的id號桩了。為了解釋清楚出現(xiàn)這個(gè)問題的原因附帽。我們先來了解一下react的中的key干了什么事情
react 中的 Key
react 中的 key 屬性,它是一個(gè)特殊的屬性井誉,它之于具有該屬性的組件或元素蕉扮,就像是身份證對于我們?nèi)艘粯樱且粋€(gè)唯一的身份標(biāo)識颗圣。一個(gè) key 對應(yīng)一個(gè)組件或者元素喳钟,具有相同的 key 的組件或元素,react 將之視為同一個(gè)組件或者元素在岂。
了解了 key 做了什么奔则,我們再回過頭看遇到的問題。我當(dāng)時(shí)遍歷數(shù)組的時(shí)候蔽午,用的 key 是數(shù)組下標(biāo)易茬。
上圖中綠色色塊的是我們新插入的元素塞耕。色塊上的數(shù)字代表 key 值舶吗,我們能很輕易的識別綠色的色塊是新加入的何吝。但 react 不這樣想敬拓。它的思路是這樣的蚊伞。
- 灰色色塊key 為 1吏恭, 更新后的灰色色塊 key 也為 1埃难,沒問題绊袋,是同一個(gè)元素叠蝇,然后將更新后的灰色色塊 id 賦值為 1璃岳,下一位。
- 米粉色色塊 key 為 2悔捶, 綠色色塊 key 也為 2铃慷, 嗯? 顏色不一樣誒蜕该,沒關(guān)系犁柜,key是一樣的,他倆就是同一個(gè)元素堂淡。于是將綠色色塊 id 賦值為 2馋缅。
... ... 以此類推 - 最后來到了最后一個(gè)元素扒腕,key 為 6 的色塊沒有誒, 那就在這里插入一個(gè)新的節(jié)點(diǎn)。然后將新建的數(shù)據(jù)賦給它萤悴,至此 over瘾腰。
- 將整個(gè)層級的 dom 比較完以后的 dom 就如下圖所示。
結(jié)果就是本應(yīng)該在2號位的數(shù)據(jù)到了6號位覆履。從而導(dǎo)致了頁面數(shù)據(jù)的異常蹋盆。
總結(jié)
如果當(dāng)涉及到遍歷的時(shí)候,頁面數(shù)據(jù)異常硝全,或者頁面與預(yù)期效果實(shí)現(xiàn)不一致栖雾,首先考慮是不是 key 值用了索引,所以伟众,為了養(yǎng)成習(xí)慣析藕,盡量避免使用索引做 key 值吧。官方也推薦我們這樣做凳厢。文章解釋得還不夠清楚噪径,后面有時(shí)間在做相應(yīng)的補(bǔ)充。
在最后数初,附上官網(wǎng)鏈接:https://zh-hans.reactjs.org/docs/lists-and-keys.html