自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

React 中的列表渲染為什么要加Key

開發(fā) 前端
對于列表的渲染,我們有必要提供 key,來對節(jié)點(diǎn)進(jìn)行區(qū)分,React 的 DOM Diff 算法會(huì)基于 key 進(jìn)行節(jié)點(diǎn)位置的調(diào)整,確保一些涉及到內(nèi)部狀態(tài)的節(jié)點(diǎn)的渲染狀態(tài)。

大家好,我是前端西瓜哥,今天來學(xué)習(xí) React 中的列表渲染要加 key 的原因。

在 React 中我們經(jīng)常需要渲染列表,比如展示好友列表。

常用寫法是用 Arrary.prototype.map 方法,將數(shù)組形式的數(shù)據(jù)映射為 JSX.Element 數(shù)組,并嵌入到組件要返回的 JSX.Element 中,如下:

function FriendList() {
const [items, setItems] = useState(['前端西瓜哥', '小明', '張三']);
return (
<ul>
{items.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
);
}

你需要給每個(gè)項(xiàng)提供 key 屬性作為標(biāo)識(shí),以區(qū)分不同的項(xiàng)。如果你不加 key,React 會(huì)警告你:

Warning: Each child in a list should have a unique "key" prop.

為什么需要 key?

在回答這個(gè)問題之前,我們先簡單了解一下 React 的 DOM Diff 算法原理。

React 會(huì)在狀態(tài)發(fā)生變化時(shí),對真實(shí) DOM 樹按需批量更新,產(chǎn)生新的 UI。

為此底層做的工作是:將新舊兩棵虛擬 DOM 樹進(jìn)行 diff 對比,計(jì)算出 patch 補(bǔ)丁,打到真實(shí) DOM 樹上。

為了高效,React 的 diff 算法做了限制:

  1. 只做同層級(jí)的節(jié)點(diǎn)對比,不跨層級(jí)比較。
  2. 如果元素的類型不同(如從 p 變成 div),那它們就是不相同的,會(huì)銷毀整個(gè)舊子樹,并調(diào)用其下組件的卸載鉤子,然后再創(chuàng)建全新的樹,相當(dāng)消耗性能。
  3. 如果類型相同,會(huì)進(jìn)行打補(bǔ)丁操作(如更新 className 和標(biāo)簽下的文本內(nèi)容)。

但這樣做會(huì)有一個(gè)問題,如果同級(jí)的多節(jié)點(diǎn) 只是位置發(fā)生了變化,但因?yàn)橄嗤饕恢脤Σ簧希职l(fā)現(xiàn)不能復(fù)用,就要銷毀一棵樹并創(chuàng)建一棵新樹,實(shí)在是太過于低效了。

于是 React 給開發(fā)者提供 key 來標(biāo)記節(jié)點(diǎn),來優(yōu)化 React diff 算法,告知 React 某個(gè)節(jié)點(diǎn)其實(shí)沒有被移除或不能被原地復(fù)用,只是換了位置而已,讓 React 更新一下位置。

列表渲染不提供 key 會(huì)怎樣?

不提供 key,React 就無法確定某個(gè)節(jié)點(diǎn)是否移動(dòng)了。

React 就只會(huì)對比相同位置的兩個(gè)節(jié)點(diǎn),如果它們類型相同(比如都是 li 元素),就會(huì)對比 props 的不同,進(jìn)行 props 的打補(bǔ)丁。

因?yàn)?nbsp;列表渲染通常都是相同的類型,所以位置變動(dòng)時(shí),多半是會(huì)觸發(fā)節(jié)點(diǎn)原地復(fù)用效果,倒是不用擔(dān)心樹的銷毀重建發(fā)生。

原地復(fù)用在不提供 key 的時(shí)候有時(shí)候也是能正確渲染的。

除了一種情況,就是 這個(gè)節(jié)點(diǎn)有自己的內(nèi)部狀態(tài),最經(jīng)典的莫過于輸入框。

function FriendList() {
const [items, setItems] = useState(['前端西瓜哥', '小明', '張三']);
const swap = () => {
[items[0], items[1]] = [items[1], items[0]];
setItems([...items]);
};
return (
<div>
<ul>
{items.map((item) => (
<li>{item}<input /></li>
))}
</ul>
<button onClick={() => { swap(); }}>
交換
</button>
</div>
);
}

我們給第一和第二個(gè)輸入框輸入內(nèi)容。

React 中的列表渲染為什么要加 key?

再點(diǎn)擊 “交換” 按鈕,交換數(shù)組第一和第二個(gè)元素位置。

然后我們看到 input 前面的文字正確交換了,但是輸入框里的內(nèi)容卻沒有交換。

React 中的列表渲染為什么要加 key?

原因是 React 做了原地復(fù)用,而 input 沒有傳 props,不需要打 props 補(bǔ)丁,保持了原樣。

這個(gè)問題怎么解決?加 key。讓 React 知道你的節(jié)點(diǎn)需要移動(dòng),你得這樣寫:

items.map((item) => (
<li key={item}>{item}<input /></li>
))

不使用 key 的另一個(gè)缺點(diǎn)是:因?yàn)樵貜?fù)用會(huì)使傳入的 props 發(fā)生變化,導(dǎo)致不能利用好 React.memo 的組件緩存能力。

列表渲染的 key 用數(shù)組索引會(huì)怎樣?

效果和不使用 key 相同,依舊是新舊節(jié)點(diǎn)的相同索引位置對比,但是控制臺(tái)不會(huì)打印警告。

應(yīng)該用什么值作為 key?

對于節(jié)點(diǎn),你需要用一個(gè)唯一的 id 賦值給 key,通常會(huì)是數(shù)組的 id,比如后端返回的好友列表的好友 id。

const [items, setItems] = useState([
{ id: 5, name: '前端西瓜哥' },
{ id: 9, name: '小明' },
{ id: 87, name: '張三' },
{ id: 91, name: '前端西瓜哥' }
]);
const list = items.map((item) => (
<li key={item.id}>{item.name}</li>
));

如果后端沒有返回 id,你可以自己手動(dòng)用一個(gè) id 生成器補(bǔ)上一個(gè) id,雖然不太優(yōu)雅就是了。比如:

const items = ['前端西瓜哥', '張三'];
const genId = (() => {
let i = 0;
return () => {
return i++;
}
})();
const itemsWithId = items.map(item => ({ id: genId(), val: item }));
// [{id: 0, val: '前端西瓜哥'}, {id: 1, val: '張三'}]

對了,這個(gè) key 只需要在同一個(gè)層級(jí)的節(jié)點(diǎn)唯一即可,不要求所有層級(jí)的 key 都是唯一的。

另外,如果你確保你的列表渲染后直到被銷毀,不會(huì)有位置上的變化,可以使用數(shù)組索引為 key。

結(jié)尾

對于列表的渲染,我們有必要提供 key,來對節(jié)點(diǎn)進(jìn)行區(qū)分,React 的 DOM Diff 算法會(huì)基于 key 進(jìn)行節(jié)點(diǎn)位置的調(diào)整,確保一些涉及到內(nèi)部狀態(tài)的節(jié)點(diǎn)的渲染狀態(tài)。

通常來說,key 值應(yīng)該是唯一的,通常來自后端返回的數(shù)據(jù)。在你確認(rèn)列表不會(huì)發(fā)生位置變更時(shí),可以使用數(shù)組索引作為 key,以去掉惱人的警告提示。

有一個(gè)點(diǎn)需要說明的是,key 并不是列表渲染的專屬,普通的節(jié)點(diǎn)也可以用 key。

責(zé)任編輯:姜華 來源: 今日頭條
相關(guān)推薦

2022-06-09 08:32:21

SQLNOLOCKWITH

2021-09-14 10:48:13

SQL Nolock代碼

2023-04-06 08:43:29

SQLWITH(NOLOCK

2022-11-15 08:35:00

SQLNOLOCK數(shù)據(jù)

2024-01-29 09:01:20

React列表模式

2020-06-16 08:17:11

代碼空格開發(fā)

2020-03-03 15:31:47

ReactVue前端

2021-07-06 07:27:45

React元素屬性

2019-08-28 16:38:49

finalJava編程語言

2022-08-15 08:27:02

基站網(wǎng)絡(luò)

2015-08-06 10:14:15

造輪子facebook

2013-03-12 14:30:09

Ubuntu操作系統(tǒng)

2023-05-09 07:16:06

2022-05-10 09:14:15

React 并發(fā)渲染

2014-08-25 10:00:18

開源

2015-05-12 11:04:42

Java EE學(xué)習(xí)Java EE

2019-01-14 07:28:56

大數(shù)據(jù)云計(jì)算互聯(lián)網(wǎng)

2019-03-19 08:59:13

物聯(lián)網(wǎng)IOT技術(shù)

2019-11-27 10:25:15

SaaS云端IT架構(gòu)

2017-04-05 16:40:45

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)