前言:最近面试经常聊到key的概念,一个非常常见的知识点,但还是遇到了一部分并不理想的情况。所以我想再啰嗦啰嗦,交流分享一下我的理解和认识。
key
这个概念,无论是React还是Vue,写过的同学,应该没有不知道的。对于key的理解,大部分人都描述的相差无二,基本都是在渲染数组列表的场景,条目项在添加、删除或调整顺序时,前后便于对应起来,提供diff以及渲染性能。
下面就以React为例唠一唠,先参考一下官网文档的说明:
“key” 是在创建元素数组时,需要用到的一个特殊字符串属性。key 帮助 React 识别出被修改、添加或删除的 item。应当给数组内的每个元素都设定 key,以使元素具有固定身份标识。
只需要保证,在同一个数组中的兄弟元素之间的 key 是唯一的。
不要将 Math.random() 之类的值传递给 key。重要的是,在前后两次渲染之间的 key 要具有“固定身份标识”的特点,以便 React 可以在添加、删除或重新排序 item 时,前后对应起来。理想情况下,key 应该从数据中获取,对应着唯一且固定的标识符,例如 post.id。
这个说道在创建元素数组时
,因为这一点,应该有相当一部分人就局限于此了。我们多想一想,这里实际上只是一个特别有代表性,而且便于理解释义的一个场景case
。多理解理解上面的解释,帮助React识别,使元素具有固定身份标识,唯一性,为什么需要这样呢,达成这样的条件,React它是想做什么?从这里我深入想一想,研究一下。
首先 SHOW ME YOUR CODE!
元素数组的case,个人认为就没有必要跟大家絮叨了,官网以及网上都能找到很多参考,这里我们拿非数组场景来研究一下。
只做核心代码逻辑展示,上下文省略,需要的自行下去完善。
定义一个示例组件: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="代表组件内部状态, 初始状态"
/>
{val ? <p>当前组件内部状态变化: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="请输入key"
value={key}
onChange={(ev) => setKey(ev.target.value)}
/>
<button onClick={() => setCkey(key)}>修改CompA实例的key</button>
<br />
<input
placeholder="请输入name"
value={name}
onChange={(ev) => setName(ev.target.value)}
/>
<button onClick={() => setCname(name)}>修改CompA实例的props: name</button>
<CompA key={ckey} name={cname} />
</div>
);
}
- 运行效果,初始状态
控制台打印内容:default CompA mounted!!!
- 修改组件状态,效果如下,ComA组件实例内部状态发生了变化
此时控制台无新的打印 - 改变一下CompA组件的props属性看看
此时组件CompA实例一直是没有变化,还是最初构建的实例对象,因为控制台依然没有新的打印。 - 现在我们改变一下key,试试看
此时控制打印内容: (第一行未#1打印的)default CompA mounted!!! porps change CompA mounted!!!
结果很明了了,#4仅仅是改变了key
,props还是name='props change'
,从打印及执行展示效果上来看,组件重新构建实例化了,props还是原来的,但因为重新实例化,内部状态没有了,回到了初始状态。这点其实在我们实际开发中很有用的,不仅限于元素数组。
线上查看:https://codesandbox.io/s/react-key-hwq6y
最终总结一下
React拿key做什么的呢,再回看官网解释, 重点我们不要仅关注元素数组,而唯一的固定身份标识
和前后对应
,这点才是核心。
在我面试的时候,很多候选人还会说到diff算法,理论上说的很多都没有问题,甚至没有破绽。但是否真正理解了diff算法以及key的概念,对应上实际case,就有一部分断线了。
React根据key,唯一确定定位对应的组件实例,通过key更快速定位,综上因此就是:
- key相同,复用保持原实例,props或者内部state状态变化,react只更新组件对应变化的属性。
- key不同,销毁之前的组件实例,再重新构建新的组件实例。(前提上下文,仅针对当前组件,组件数组情况可对比参考官网)
技术最重要的还是应用,要灵活应用好,首先我们要真正的理解它是怎么运行的,进而逻辑原理,最终能做到触类旁通,举一反三,知其然,知其所以然。
参考
个人理解整理,欢迎交流,如有错误之处,还请斧正。