React組件中的Key

概述

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對性能是有幫組的

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蒙揣,一起剝皮案震驚了整個濱河市靶溜,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌懒震,老刑警劉巖罩息,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異个扰,居然都是意外死亡瓷炮,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進(jìn)店門递宅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來娘香,“玉大人,你說我怎么就攤上這事办龄『嬲溃” “怎么了?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵俐填,是天一觀的道長安接。 經(jīng)常有香客問我,道長英融,這世上最難降的妖魔是什么盏檐? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮驶悟,結(jié)果婚禮上胡野,老公的妹妹穿的比我還像新娘。我一直安慰自己撩银,他們只是感情好给涕,可當(dāng)我...
    茶點故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著额获,像睡著了一般够庙。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上抄邀,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天耘眨,我揣著相機與錄音,去河邊找鬼境肾。 笑死剔难,一個胖子當(dāng)著我的面吹牛胆屿,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播偶宫,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼非迹,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了纯趋?” 一聲冷哼從身側(cè)響起憎兽,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎吵冒,沒想到半個月后纯命,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡痹栖,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年亿汞,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片揪阿。...
    茶點故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡疗我,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出图甜,到底是詐尸還是另有隱情碍粥,我是刑警寧澤,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布黑毅,位于F島的核電站嚼摩,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏矿瘦。R本人自食惡果不足惜枕面,卻給世界環(huán)境...
    茶點故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望缚去。 院中可真熱鬧潮秘,春花似錦、人聲如沸易结。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽搞动。三九已至躏精,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間鹦肿,已是汗流浹背矗烛。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留箩溃,地道東北人瞭吃。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓酣难,卻偏偏與公主長得像膨报,于是被迫代替她去往敵國和親蒿涎。 傳聞我的和親對象是個殘疾皇子汽纠,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,864評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 3. JSX JSX是對JavaScript語言的一個擴展語法媳危, 用于生產(chǎn)React“元素”烦周,建議在描述UI的時候...
    pixels閱讀 2,824評論 0 24
  • 原教程內(nèi)容詳見精益 React 學(xué)習(xí)指南帚稠,這只是我在學(xué)習(xí)過程中的一些閱讀筆記孵运,個人覺得該教程講解深入淺出穆律,比目前大...
    leonaxiong閱讀 2,834評論 1 18
  • 作為一個合格的開發(fā)者惠呼,不要只滿足于編寫了可以運行的代碼。而要了解代碼背后的工作原理峦耘;不要只滿足于自己的程序...
    六個周閱讀 8,448評論 1 33
  • HTML模版 之后出現(xiàn)的React代碼嵌套入模版中剔蹋。 1. Hello world 這段代碼將一個一級標(biāo)題插入到指...
    ryanho84閱讀 6,232評論 0 9
  • 40、React 什么是React辅髓?React 是一個用于構(gòu)建用戶界面的框架(采用的是MVC模式):集中處理VIE...
    萌妹撒閱讀 1,016評論 0 1