日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
React 中的列表渲染為什么要加Key

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

創(chuàng)新互聯(lián)專注于雙峰企業(yè)網(wǎng)站建設(shè),響應(yīng)式網(wǎng)站,成都商城網(wǎng)站開發(fā)。雙峰網(wǎng)站建設(shè)公司,為雙峰等地區(qū)提供建站服務(wù)。全流程定制網(wǎng)站,專業(yè)設(shè)計,全程項目跟蹤,創(chuàng)新互聯(lián)專業(yè)和態(tài)度為您提供的服務(wù)

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

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

function FriendList() {
const [items, setItems] = useState(['前端西瓜哥', '小明', '張三']);
return (

    {items.map((item) => (
  • {item}

  • ))}

);
}

你需要給每個項提供 key 屬性作為標識,以區(qū)分不同的項。如果你不加 key,React 會警告你:

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

為什么需要 key?

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

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

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

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

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

但這樣做會有一個問題,如果同級的多節(jié)點 只是位置發(fā)生了變化,但因為相同索引位置對不上,又發(fā)現(xiàn)不能復(fù)用,就要銷毀一棵樹并創(chuàng)建一棵新樹,實在是太過于低效了。

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

列表渲染不提供 key 會怎樣?

不提供 key,React 就無法確定某個節(jié)點是否移動了。

React 就只會對比相同位置的兩個節(jié)點,如果它們類型相同(比如都是 li 元素),就會對比 props 的不同,進行 props 的打補丁。

因為 列表渲染通常都是相同的類型,所以位置變動時,多半是會觸發(fā)節(jié)點原地復(fù)用效果,倒是不用擔心樹的銷毀重建發(fā)生。

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

除了一種情況,就是 這個節(jié)點有自己的內(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 (


    {items.map((item) => (
  • {item}

  • ))}



);
}

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

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

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

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

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

items.map((item) => (
  • {item}

  • ))

    不使用 key 的另一個缺點是:因為原地復(fù)用會使傳入的 props 發(fā)生變化,導(dǎo)致不能利用好 React.memo 的組件緩存能力。

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

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

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

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

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

  • ));

    如果后端沒有返回 id,你可以自己手動用一個 id 生成器補上一個 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: '張三'}]

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

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

    結(jié)尾

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

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

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


    分享標題:React 中的列表渲染為什么要加Key
    轉(zhuǎn)載注明:http://www.dlmjj.cn/article/dpieoos.html