前言:最近面試經(jīng)常聊到key的概念,一個(gè)非常常見的知識(shí)點(diǎn),但還是遇到了一部分并不理想的情況。所以我想再啰嗦啰嗦,交流分享一下我的理解和認(rèn)識(shí)。
key這個(gè)概念,無(wú)論是React還是Vue,寫過(guò)的同學(xué),應(yīng)該沒有不知道的。對(duì)于key的理解,大部分人都描述的相差無(wú)二,基本都是在渲染數(shù)組列表的場(chǎng)景,條目項(xiàng)在添加、刪除或調(diào)整順序時(shí),前后便于對(duì)應(yīng)起來(lái),提供diff以及渲染性能。
下面就以React為例嘮一嘮,先參考一下官網(wǎng)文檔的說(shuō)明:
“key” 是在創(chuàng)建元素?cái)?shù)組時(shí),需要用到的一個(gè)特殊字符串屬性。key 幫助 React 識(shí)別出被修改、添加或刪除的 item。應(yīng)當(dāng)給數(shù)組內(nèi)的每個(gè)元素都設(shè)定 key,以使元素具有固定身份標(biāo)識(shí)。
只需要保證,在同一個(gè)數(shù)組中的兄弟元素之間的 key 是唯一的。
不要將 Math.random() 之類的值傳遞給 key。重要的是,在前后兩次渲染之間的 key 要具有“固定身份標(biāo)識(shí)”的特點(diǎn),以便 React 可以在添加、刪除或重新排序 item 時(shí),前后對(duì)應(yīng)起來(lái)。理想情況下,key 應(yīng)該從數(shù)據(jù)中獲取,對(duì)應(yīng)著唯一且固定的標(biāo)識(shí)符,例如 post.id。
這個(gè)說(shuō)道在創(chuàng)建元素?cái)?shù)組時(shí),因?yàn)檫@一點(diǎn),應(yīng)該有相當(dāng)一部分人就局限于此了。我們多想一想,這里實(shí)際上只是一個(gè)特別有代表性,而且便于理解釋義的一個(gè)場(chǎng)景case。多理解理解上面的解釋,幫助React識(shí)別,使元素具有固定身份標(biāo)識(shí),唯一性,為什么需要這樣呢,達(dá)成這樣的條件,React它是想做什么?從這里我深入想一想,研究一下。
首先 SHOW ME YOUR CODE!
元素?cái)?shù)組的case,個(gè)人認(rèn)為就沒有必要跟大家絮叨了,官網(wǎng)以及網(wǎng)上都能找到很多參考,這里我們拿非數(shù)組場(chǎng)景來(lái)研究一下。
只做核心代碼邏輯展示,上下文省略,需要的自行下去完善。
定義一個(gè)示例組件:CompA.js
import { useEffect, useState } from "react";
export default function CompA({ name }) {
const [val, setVal] = useState("");
useEffect(() => {
console.log(name, "mounted!!!");
}, []);
return (
<div className="App">
<h2>CompA name: {name}</h2>
<input
style={{ width: "200px" }}
value={val}
onChange={(ev) => setVal(ev.target.value)}
placeholder="代表組件內(nèi)部狀態(tài), 初始狀態(tài)"
/>
{val ? <p>當(dāng)前組件內(nèi)部狀態(tài)變化:val = {val}</p> : null}
</div>
);
}
示例展示容器: App.js
import "./styles.css";
import CompA from "./CompA";
import { useState } from "react";
export default function App() {
const [key, setKey] = useState("");
const [name, setName] = useState("");
const [cname, setCname] = useState("default");
const [ckey, setCkey] = useState("a1");
return (
<div className="App">
<h1>React key</h1>
<input
placeholder="請(qǐng)輸入key"
value={key}
onChange={(ev) => setKey(ev.target.value)}
/>
<button onClick={() => setCkey(key)}>修改CompA實(shí)例的key</button>
<br />
<input
placeholder="請(qǐng)輸入name"
value={name}
onChange={(ev) => setName(ev.target.value)}
/>
<button onClick={() => setCname(name)}>修改CompA實(shí)例的props: name</button>
<CompA key={ckey} name={cname} />
</div>
);
}
- 運(yùn)行效果,初始狀態(tài)
運(yùn)行效果,初始狀態(tài)
控制臺(tái)打印內(nèi)容:default CompA mounted!!! - 修改組件狀態(tài),效果如下,ComA組件實(shí)例內(nèi)部狀態(tài)發(fā)生了變化
修改組件狀態(tài),展示效果
此時(shí)控制臺(tái)無(wú)新的打印 - 改變一下CompA組件的props屬性看看
修改props,展示效果
此時(shí)組件CompA實(shí)例一直是沒有變化,還是最初構(gòu)建的實(shí)例對(duì)象,因?yàn)榭刂婆_(tái)依然沒有新的打印。 - 現(xiàn)在我們改變一下key,試試看
修改key,展示效果
此時(shí)控制打印內(nèi)容: (第一行未#1打印的)default CompA mounted!!! porps change CompA mounted!!!
結(jié)果很明了了,#4僅僅是改變了key,props還是name='props change',從打印及執(zhí)行展示效果上來(lái)看,組件重新構(gòu)建實(shí)例化了,props還是原來(lái)的,但因?yàn)橹匦聦?shí)例化,內(nèi)部狀態(tài)沒有了,回到了初始狀態(tài)。這點(diǎn)其實(shí)在我們實(shí)際開發(fā)中很有用的,不僅限于元素?cái)?shù)組。
線上查看:https://codesandbox.io/s/react-key-hwq6y
最終總結(jié)一下
React拿key做什么的呢,再回看官網(wǎng)解釋, 重點(diǎn)我們不要僅關(guān)注元素?cái)?shù)組,而唯一的固定身份標(biāo)識(shí)和前后對(duì)應(yīng),這點(diǎn)才是核心。
在我面試的時(shí)候,很多候選人還會(huì)說(shuō)到diff算法,理論上說(shuō)的很多都沒有問(wèn)題,甚至沒有破綻。但是否真正理解了diff算法以及key的概念,對(duì)應(yīng)上實(shí)際case,就有一部分?jǐn)嗑€了。
React根據(jù)key,唯一確定定位對(duì)應(yīng)的組件實(shí)例,通過(guò)key更快速定位,綜上因此就是:
- key相同,復(fù)用保持原實(shí)例,props或者內(nèi)部state狀態(tài)變化,react只更新組件對(duì)應(yīng)變化的屬性。
- key不同,銷毀之前的組件實(shí)例,再重新構(gòu)建新的組件實(shí)例。(前提上下文,僅針對(duì)當(dāng)前組件,組件數(shù)組情況可對(duì)比參考官網(wǎng))
技術(shù)最重要的還是應(yīng)用,要靈活應(yīng)用好,首先我們要真正的理解它是怎么運(yùn)行的,進(jìn)而邏輯原理,最終能做到觸類旁通,舉一反三,知其然,知其所以然。
參考
個(gè)人理解整理,歡迎交流,如有錯(cuò)誤之處,還請(qǐng)斧正。



