概述
React
的初學(xué)者在寫React
項目的過程中闹丐,經(jīng)常會在寫一個列表組件的時候黄伊,發(fā)現(xiàn)控制臺拋出了如下的Warning
Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of ServiceInfo. See [https://fb.me/react-warning-keys](https://fb.me/react-warning-keys) for more information.
提示每一個創(chuàng)建的子組件必須有key屬性捉兴,那么這個key具體是做什么的呢抡蛙?
在React中臭胜,key不是給用戶使用的莫其,而是給React自己使用的,一般來說耸三,當(dāng)我們動態(tài)創(chuàng)建React的元素乱陡,并且React元素內(nèi)包含數(shù)量或順序不確定的子元素時,我們就需要提供 key 這個特殊的屬性仪壮。
Diff算法和key的關(guān)系
標(biāo)準(zhǔn)的diff算法時間復(fù)雜度是O(n^3)
,如果你的項目中有1000個元素憨颠,則需要1000000000次比對,這種效率是極其低下的积锅,而React的diff算法不同爽彤,它的時間復(fù)雜度為O(n)
,但這種時間復(fù)雜度建立在兩個基礎(chǔ)之上:
- 不同類型的元素產(chǎn)生不同的虛擬DOM樹
- 開發(fā)人員可以通過不同的key屬性來標(biāo)識哪些子元素在不同的渲染環(huán)境中需要保持穩(wěn)定缚陷。
針對第一種情況:
// A組件
<div>
<Todos />
</div>
// B組件
<span>
<Todos />
</span>
如果我們現(xiàn)在想把A組件更新成B組件适篙,React在做比較的時候,發(fā)現(xiàn)最外層節(jié)點類型不同箫爷,則直接廢棄A組件嚷节,重新創(chuàng)建B組件聂儒,哪怕里面的子組件是一樣的,雖然這是一種巨大的浪費硫痰,但為了達(dá)到O(n)
的時間復(fù)雜度衩婚,只能采用這種方式,在開發(fā)項目過程中效斑,我們要避免更改節(jié)點的包裹類型
針對第二種情況
this.state = {
users: [{id:1,name: '張三'}, {id:2, name: '李四'}, {id: 2, name: "王五"}],
....//省略
}
render()
return(
<div>
<h3>用戶列表</h3>
{this.state.users.map(u => <div key={u.id}>{u.id}:{u.name}</div>)}
</div>
)
);
上面的代碼在DOM掛載渲染后非春,只會展示張三和李四兩個用戶,王五并沒有展示缓屠,是因為李四的key值和王五相同奇昙,此時React會認(rèn)為這是一個相同的組件,所以不去渲染藏研。
所以在有了Key屬性后敬矩,React就會通過key來決定是重新創(chuàng)建一個組件還是更新原來的組件。
- key值相同:組件屬性發(fā)生變化蠢挡,React只更新對應(yīng)組件的屬性
- key值不同:React先銷毀原來的組件弧岳,然后在重新創(chuàng)建新的組件
key值要穩(wěn)定唯一
在項目開發(fā)中,key屬性的使用場景最多的還是由數(shù)組動態(tài)創(chuàng)建的子組件的情況 ,
我們要保證key要穩(wěn)定唯一
可以把key看做每個人的身份中號业踏,也可以看做數(shù)據(jù)庫中的主鍵禽炬,都是用來作為唯一標(biāo)識的。
因此勤家,如果你采用下面的方式創(chuàng)建key值
{
this.state.data.map(el=><MyComponent key={Math.random()}/>)
}
是完全錯誤的腹尖,因為Math.random
隨機生成,不穩(wěn)定
盡量不要用數(shù)組的index去作為key
之所以這里說的是盡量伐脖,是因為如果你List中的子組件如果只是負(fù)責(zé)純展示的話热幔,(不涉及數(shù)組的動態(tài)變更),那么是可以采用index
作為key的讼庇。
但如果組件設(shè)計到數(shù)組的動態(tài)變更绎巨,例如數(shù)組新增元素、刪除元素或者重新排序等蠕啄,這時index作為key會導(dǎo)致展示錯誤的數(shù)據(jù)场勤。
{this.state.data.map((v,index)=><Item key={index} v={v} />)}
// 開始時:['a','b','c']=>
<ul>
<li key="0">a <input type="text"/></li>
<li key="1">b <input type="text"/></li>
<li key="2">c <input type="text"/></li>
</ul>
//input輸入框分別輸入 1,2,3
// 數(shù)組重排 -> ['c','b','a'] =>
<ul>
<li key="0">c <input type="text"/></li>
<li key="1">b <input type="text"/></li>
<li key="2">a <input type="text"/></li>
</ul>
//input 輸入框還是 1,2,3 并未隨著數(shù)組重排而改變
在上面的代碼中,key值采用index歼跟,在數(shù)據(jù)重新排列后:
- 組件重新render得到新的虛擬DOM
- 新的虛擬DOM和之前的虛擬DOM進(jìn)行比較和媳,發(fā)現(xiàn)都有key=0的組件,此時React認(rèn)為這是同一個組件哈街,因此只會更新組件留瞳,不會銷毀在重新創(chuàng)建
- 然后比較children,發(fā)現(xiàn)文本內(nèi)容不同 a?c 但input組件沒發(fā)生變化叹卷,此時觸發(fā)
componentWillReceiveProps
方法撼港,從而更新其子組件文本內(nèi)容 - 因為組件的children中input組件沒有變化坪它,其又與父組件傳入的任props沒有關(guān)聯(lián)骤竹,所以input組件不會更新(即其
componentWillReceiveProps
方法不會被執(zhí)行)帝牡,導(dǎo)致用戶輸入的值不會變化
最后要特別說明的一個點是:
key不是用來提升react的性能的,不過用好key對性能是有幫組的