前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >react中key的正确使用方式

react中key的正确使用方式

作者头像
ConardLi
发布于 2019-09-08 14:55:29
发布于 2019-09-08 14:55:29
3K00
代码可运行
举报
文章被收录于专栏:code秘密花园code秘密花园
运行总次数:0
代码可运行

key的原理?为了么要使用key?选什么做key?

在开发react程序时我们经常会遇到这样的警告,然后就会想到:哦!循环子组件忘记加key了~

出于方便,有时候会不假思索的使用循环的索引作为key,但是这样真的好吗?什么样的值才是key的最佳选择?

为了弄明白,本文将从三个方面来分析"key":

1.为什么要使用key

2.使用index做key存在的问题

3.正确的选择key

1.为什么要使用key

react官方文档是这样描述key的:

Keys可以在DOM中的某些元素被增加或删除的时候帮助React识别哪些元素发生了变化。因此你应当给数组中的每一个元素赋予一个确定的标识。

react的diff算法是把key当成唯一id然后比对组件的value来确定是否需要更新的,所以如果没有key,react将不会知道该如何更新组件。

你不传key也能用是因为react检测到子组件没有key后,会默认将数组的索引作为key。

react根据key来决定是销毁重新创建组件还是更新组件,原则是:

  • key相同,组件有所变化,react会只更新组件对应变化的属性。
  • key不同,组件会销毁之前的组件,将整个组件重新渲染。

2.使用index做key存在的问题

2.1 受控组件

单纯的展示组件比如span,这些组件是受控组件,意味着他们的值将是我们给定好的。

如果子组件只是受控组件,使用index作为key,可能表面上不会有什么问题,实际上性能会受很大的影响。例如下面的代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// ['张三','李四','王五']=>
<ul>
   <li key="0">张三</li>
   <li key="1">李四</li>
   <li key="2">王五</li>
</ul>
// 数组重排 -> ['王五','张三','李四'] =>
<ul>
   <li key="0">王五</li>
   <li key="1">张三</li>
   <li key="2">李四</li>
</ul>

当元素数据源的顺序发生改变时,对应的:

key为0,1,2的组件都发生了变化,三个子组件都会被重新渲染。(这里的重新渲染不是销毁,因为key还在)

相反,我们使用唯一id作为key:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// ['张三','李四','王五']=>
<ul>
   <li key="000">张三</li>
   <li key="111">李四</li>
   <li key="222">王五</li>
</ul>
// 数组重排 -> ['王五','张三','李四'] =>
<ul>
   <li key="222">王五</li>
   <li key="000">张三</li>
   <li key="111">李四</li>
</ul>

根据上面的更新原则,子组件的值和key均未发生变化,只是顺序发生改变,因此react只是将他们做了移动,并未重新渲染。

2.2 非受控组件

像input这样可以由用户任意改变值,不受我们控制的组件,在使用了index作为key时可能会发生问题,看如下的例子:

子组件:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  render() {    return (      <div>
       <p >值:{this.props.value}</p>
       <input />
     </div>
   );
 }
}

父组件

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
{
this.state.data.map((element, index) => {
   return <Child value={element} key={index} />
   })
}

我们在前两个输入框分别输入对应的值:

然后在头部添加一个元素:

很明显,这个结果并不符合我们的预期,我们来分析一下发生了什么:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<div key="0">
   <p >值:0</p>
   <input />
</div>
<div key="1">
   <p >值:1</p>
   <input />
</div>
<div key="2">
   <p >值:2</p>
   <input />
</div>

变化后:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<div key="0">
   <p >值:5</p>
   <input />
</div>
<div key="1">
   <p >值:0</p>
   <input />
</div>
<div key="2">
   <p >值:1</p>
   <input />
</div>
<div key="3">
   <p >值:2</p>
   <input />
</div>

可以发现:key 0,1,2并没有发生改变,根据规则,不会卸载组件,只会更新改变的属性。

react只diff到了p标签内值的变化,而input框中的值并未发生改变,因此不会重新渲染,只更新的p标签的值。

当使用唯一id作为key后:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<div key="000">
   <p >值:0</p>
   <input />
</div>
<div key="111">
   <p >值:1</p>
   <input />
</div>
<div key="222">
   <p >值:2</p>
   <input />
</div>

变化后:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<div key="555">
   <p >值:5</p>
   <input />
</div>
<div key="000">
   <p >值:0</p>
   <input />
</div>
<div key="111">
   <p >值:1</p>
   <input />
</div>
<div key="222">
   <p >值:2</p>
   <input />
</div>

可以很明显的发现:key为 111,222,333的组件没有发生任何改变,react不会更新他们,只是新插入了子组件555,并改变了其他组件的位置。

3.正确的选择key

3.1 纯展示

如果组件单纯的用于展示,不会发生其他变更,那么使用index或者其他任何不相同的值作为key是没有任何问题的,因为不会发生diff,就不会用到key。

3.2 推荐使用index的情况

并不是任何情况使用index作为key会有缺陷,比如如下情况:

你要分页渲染一个列表,每次点击翻页会重新渲染:

使用唯一id:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
第一页<ul>
   <li key="000">张三</li>
   <li key="111">李四</li>
   <li key="222">王五</li>
</ul>第二页<ul>
   <li key="333">张三三</li>
   <li key="444">李四四</li>
   <li key="555">王五五</li>
</ul>

翻页后,三条记录的key和组件都发生了改变,因此三个子组件都会被卸载然后重新渲染。

使用index:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
第一页<ul>
   <li key="0">张三</li>
   <li key="1">李四</li>
   <li key="2">王五</li>
</ul>第二页<ul>
   <li key="0">张三三</li>
   <li key="1">李四四</li>
   <li key="2">王五五</li>
</ul>

翻页后,key不变,子组件值发生改变,组件并不会被卸载,只发生更新。

3.3 子组件可能发生变更/使用了非受控组件

大多数情况下,使用唯一id作为子组件的key是不会有任何问题的。

这个id一定要是唯一,并且稳定的,意思是这条记录对应的id一定是独一无二的,并且永远不会发生改变。

不推荐使用math.random或者其他的第三方库来生成唯一值作为key。

因为当数据变更后,相同的数据的key也有可能会发生变化,从而重新渲染,引起不必要的性能浪费。

如果数据源不满足我们这样的需求,我们可以在渲染之前为数据源手动添加唯一id,而不是在渲染时添加。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-11-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 code秘密花园 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
WebSocket 教程
WebSocket 是一种网络通信协议,很多高级功能都需要它。 本文介绍 WebSocket 协议的使用方法。 一、为什么需要 WebSocket? 初次接触 WebSocket 的人,都会问同样的问
ruanyf
2018/04/13
1.9K0
WebSocket 教程
WebSocket 详解
WebSocket 协议在2008年诞生,2011年成为国际标准。所有浏览器都已经支持了。它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。
EchoROne
2022/08/15
8290
websocket深入浅出
答: 它是一种网络通信协议,是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。
全栈程序员站长
2022/09/14
2.3K0
websocket深入浅出
websocket 协议解析
IMWeb前端团队
2018/01/08
1.3K0
websocket 协议解析
WebSocket 简介及应用实例
HTML5 的出现,标志着后 Flash 时代各种现代浏览器的集体爆发,也是谨防 Adobe 一家独大的各家厂商们,历经多年各自为战,想换个活法儿并终于达成一定共识后,所积kao累bei的技术的一次集中释放 -- 正所谓 “H5 是个筐,什么都可以往里装”。
江米小枣
2020/06/16
1.3K0
WebSocket 简介及应用实例
5000字!带你零距离接触websocket!
原文 https://juejin.im/post/6876301731966713869
lucifer210
2020/10/23
5190
5000字!带你零距离接触websocket!
WebSocket 浅析
前言 在WebSocket API尚未被众多浏览器实现和发布的时期,开发者在开发需要接收来自服务器的实时通知应用程序时,不得不求助于一些“hacks”来模拟实时连接以实现实时通信,最流行的一种方式是长轮询 。 长轮询主要是发出一个HTTP请求到服务器,然后保持连接打开以允许服务器在稍后的时间响应(由服务器确定)。为了这个连接有效地工作,许多技术需要被用于确保消息不错过,如需要在服务器端缓存和记录多个的连接信息(每个客户)。虽然长轮询是可以解决这一问题的,但它会耗费更多的资源,如CPU、内存和带宽等,要想很好
腾讯Bugly
2018/03/23
2.7K0
websocket
短轮询(Polling)的实现思路就是 浏览器端 每隔几秒钟向 服务器端 发送http请求,服务端在收到请求后,不论是否有数据更新,都直接进行响应。 在服务端响应完成,就会关闭这个Tcp连接 ,如下图所示:
用户10106350
2022/10/28
2.7K0
WebSocket安全性分析
WebSocket 是HTML5一种新的网络传输协议,位于 OSI 模型的应用层,可在单个TCP连接上进行全双工通信。WebSocket 建议于 TCP 协议之上,与 HTTP 协议有良好的兼容性。协议标识符是ws;如果加密,则为wss。
FB客服
2023/08/08
4160
WebSocket安全性分析
HTML5(十一)——WebSocket 基础教程
websocket 是 HTML5 提供的一种长链接双向通讯协议,使得客户端和服务器之间的数据交换更简单,允许服务端主动向客户端推送数据,并且客户端与服务端只需连接一次,就可以保持长久连接,并进行数据通信。
呆呆
2021/09/30
1K0
WebSocket系列之基础知识入门篇
本文是WebSocket系列的第一篇,主要介绍WebSocket相关的基础协议知识和API。由于WebSocket的相关介绍在MDN中分布较乱,初学者不太容易入门,因此通过本文将相关基础知识和使用方法进行一个归纳和总结。
黄Java
2018/09/18
8320
WebSocket协议 8 问
WebSocket是一种比较新的协议,它是伴随着html5规范而生的,虽然还比较年轻,但大多主流浏览器都已经支持。它使用方便、应用广泛,已经渗透到前后端开发的各种场景中。
xjjdog
2019/09/24
9400
WebSocket协议 8 问
《深入浅出Node.js》-WebSocket
WebSocket 与 Node 之间的配合可以说是天作之合:WebSocket 客户端基于事件的编程模型与 Node 中自定义事件相差无几;WebSocket 实现了客户端与服务器之间的长连接,而 Node 在与大量客户端之间保持高并发连接方面非常擅长。
李振
2021/11/26
1.6K0
《深入浅出Node.js》-WebSocket
全双工通信的 WebSocket
WebSocket 是一种网络通信协议。在 2009 年诞生,于 2011 年被 IETF 定为标准 RFC 6455 通信标准。并由 RFC7936 补充规范。WebSocket API 也被 W3C 定为标准。
一缕殇流化隐半边冰霜
2018/08/30
2.3K0
全双工通信的 WebSocket
WebSocket 原理浅析与实现简单聊天
随着 Web 的发展,用户对于 Web 的实时推送要求也越来越高,在 WebSocket 出现之前,大多数情况下是通过客户端发起轮询来拿到服务端实时更新的数据,因为 HTTP1.x 协议有一个缺陷就是通信只能由客户端发起,服务端没法主动给客户端推送。
用户4962466
2019/12/12
7600
Websocket 简介
  随着网际网络的发展,传统的HTTP协议已经很难满足Web应用日益复杂的需求了。近年来,随着HTML5的完善,WebSocket协议被提出,它实现了浏览器与服务器的全双工通讯,扩充套件了浏览器与服务端的通讯功能,使服务端也能主动向客户端传送资料。
老猫-Leo
2023/12/11
5270
Websocket 简介
一遍就能读懂的WebSocket协议详解
WebSocket提供了实时的、双向的通信机制,可以立即将数据从服务器推送到客户端,实现即时更新。
用户6297767
2023/12/14
1.4K0
一遍就能读懂的WebSocket协议详解
带你揭开WebSocket的神秘面纱!
在揭开webSocket的神秘面纱之前,有言在先,我在写文章之前对webSocket一无所知,由于公司业务用到,故此研究记录一下,班门弄斧之处,请大佬批评指正!
用户7413032
2020/06/11
1.1K0
带你揭开WebSocket的神秘面纱!
「IM系列」WebSocket实践教程:基础入门实战
WebSocket是一种通信协议,可在单个TCP连接上进行全双工通信。WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就可以建立持久性的连接,并进行双向数据传输。
Tinywan
2023/12/03
5460
「IM系列」WebSocket实践教程:基础入门实战
传统轮询、长轮询、服务器发送事件与WebSocket
构建网络应用的过程中,我们经常需要与服务器进行持续的通讯以保持双方信息的同步。通常这种持久通讯在不刷新页面的情况下进行,消耗一定的内存资源常驻后台,并且对于用户不可见。本文将简要介绍Web通信中常用的四种方式。
kirin
2021/05/08
3.1K0
相关推荐
WebSocket 教程
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验