React中key的必要性与使用
React这个框架的核心思想是,将页面分割成一个个组件,一个组件还可能嵌套更小的组件,每个组件有自己的数据(属性/状态);当某个组件的数据发生变化时,更新该组件部分的视图。更新的过程是由数据驱动的,新的数据自该组件顶层向下流向子组件,每个组件调用自己的render方法得到新的视图,并与之前的视图作diff-比较差异,完成更新。这个过程就叫作reconciliation-调和。
React通过virtual dom来实现高效的视图更新。基本原理是用纯js对象模拟dom树,每当更新时,根据组件们的render方法计算出新的虚拟dom树,并与此前的虚拟dom树作diff,得到一个patch(差异补丁),最后映射到真实dom树上完成视图更新。而两棵树的完全的 diff 算法是一个时间复杂度为 O(n^3) 的问题。但是在前端当中,很少出现跨越层级移动DOM元素的情况,所以React采用了简化的diff算法,只会对virtual dom中同一个层级的元素进行对比,这样算法复杂度就可以达到 O(n)。
由于React采用的diff算法是对新旧虚拟dom树同层级的元素挨个比较,碰到循环输出的元素时会有一些问题,比如列表。先来看一个例子:
// 旧v-dom
<ul>
<li>first</li>
<li>second</li>
</ul>
// 新v-dom
<ul>
<li>zero</li>
<li>first</li>
<li>second</li>
</ul>
React在diff两棵树时,发现原来的两个li元素都与新v-dom中对应位置上的两个li元素不同,就会对其修改,并向真实dom树中插入新的second节点。实际上,我们可能只是进行了在first之前插入新zero节点的操作,而现在进行了额外的修改操作。
React官方文档提示我们应该使用key属性来解决上述问题。key是一个字符串,用来唯一标识同父同层级的兄弟元素。当React作diff时,只要子元素有key属性,便会去原v-dom树中相应位置(当前横向比较的层级)寻找是否有同key元素,比较它们是否完全相同,若是则复用该元素,免去不必要的操作。
延续第一个例子,如果每个li元素都有key属性:
// 旧v-dom
<ul>
<li key="1">first</li>
<li key="2">second</li>
</ul>
// 新v-dom
<ul>
<li key="0">zero</li>
<li key="1">first</li>
<li key="2">second</li>
</ul>
现在React就知道了,新增了key为"0"的元素,而"1"与"2"仅仅移动了位置。
key必须是字符串类型,它的取值可以用数据对象的某个唯一属性,或是对数据进行hash来生成key。
<ul>
{list.map(v=> <li key={v.idProp}>{v.text}</li>)}
</ul>
但是强烈不推荐用数组index来作为key。如果数据更新仅仅是数组重新排序或在其中间位置插入新元素,那么视图元素都将重新渲染。来看下例子:
<ul>{list.map((v,idx)=><li key={idx}>{v}</li>)}</ul>
// ['a','b','c']=>
<ul>
<li key="0">a</li>
<li key="1">b</li>
<li key="2">c</li>
</ul>
// 数组重排 -> ['c','a','b'] =>
<ul>
<li key="0">c</li>
<li key="1">a</li>
<li key="2">b</li>
</ul>
React发现key为0,1,2的元素的text都变了,将会修改三者的html,而不是移动它们。
渲染同类型元素不带key只会产生性能问题,如果渲染的是不同类型的状态性组件,组件将会被替换,状态丢失。
class Box extends React.Component {
constructor(p) {
super(p)
this.state = {type: true}
this.handler = this.handler.bind(this)
}
handler() {
this.setState({
type: !this.state.type
})
}
render() {
return (<div>
<button onClick={this.handler}>haha</button>
{this.state.type ?
(<div><Son_1 /><Son_2 /></div>)
: (<div><Son_2 /><Son_1 /></div>)
}
</div>)
}
}
如上述代码,每次按下按钮,原Son_1与Son_2组件的实例都将被销毁,并创建新的Son_1与Son_2实例,不能继承原来的状态;而它们实际上只是调换了位置。给它们加上key可以避免问题:
{this.state.type ?
(<div><Son_1 key="1"/><Son_2 key="2"/></div>)
: (<div><Son_2 key="2"/><Son_1 key="1"/></div>)
}
所以,碰到数组->列表的映射,或是同级元素需要移位的情况,一定要给元素加上key属性!
结论:用key可以提升react的性能,但是在很多地方维护key会增大复制操作业务的复杂程度,需根据情况权衡
React中key的必要性与使用的更多相关文章
- 【react】---react中key值的作用
一.React中key值得作用 react中的key属性,它是一个特殊的属性,它是出现不是给开发者用的,而是给React自己使用,有了key属性后,就可以与组件建立了一种对应关系,简单说,react利 ...
- vue中:key 和react 中key={} 的作用,以及ref的特性?
vue中:key 和react 中key={} 为了给 vue 或者react 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key 属性 一句话概括就是 ...
- React中key的讲解
通过阅读React的文档我们知道React这个框架的核心思想是,将页面分割成一个个组件,一个组件还可能嵌套更小的组件,每个组件有自己的数据(属性/状态);当某个组件的数据发生变化时,更新该组件部分的视 ...
- react中key值的理解
react利用key来识别组件,它是一种身份标识标识,相同的key react认为是同一个组件,这样后续相同的key对应组件都不会被创建有了key属性后,就可以与组件建立了一种对应关系,react根据 ...
- react中key的使用
面试题: 1). react/vue中的key的作用/内部原理 2). 为什么列表的key尽量不要用index 虚拟DOM的key的作用? 1). 简单的说: key是虚拟DOM对象的标识, 在更新显 ...
- React之事件绑定、列表中key的使用
在学习React的Hadding Events这一章节,发现事件回调函数的几种写法,看似区别不大,但实际差异还是蛮大的. class Toggle extends React.Component{ c ...
- React 中的key值
在react中必须要有key值,key不是用来提升react的性能的,react中的key属性,它是一个特殊的属性,它是出现不是给开发者用的(例如你为一个组件设置key之后不能获取组件的这个key p ...
- 谈谈Vue/React中的虚拟DOM(vDOM)与Key值
谈谈Vue/React中的虚拟DOM(vDOM)与Key值 一.DocumentFragment 在了解虚拟DOM前,先来了解DOM的一个对象属性--DocumentFragment. 在一次操作中, ...
- DIFF算法浅析(三)在react中的实现
在虚拟dom中diff的实现. 分别从4个方面: DIFF抽象概念(概述.时间复杂性分析) 在Vue2中的实现(版本2.6.11.必要性.执行方式) 在React中的实现(版本16.13.1,必要性. ...
随机推荐
- 复制View对象
Mark一下 - (UIView*)duplicate:(UIView*)view { NSData * tempArchive = [NSKeyedArchiver archivedDataWit ...
- 一个.java文件定义多个类的情况
一个.java文件中定义多个类: 注意一下几点: (1) public权限类只能有一个(也可以一个都没有,但最多只有一个): (2)这个.java文件名只能是public 权限的类的类名: (3)倘若 ...
- phpexcel--导入excel表格
最近在做一个小项目,就是一个管理信息的小系统:要求有导入和导出的信息为excel的功能,研究过导入导出功能的肯定知道导出要比导入的简单多了,导入用的phpexcel,当时对phpexcel是完全不了解 ...
- Android 利用工具实现一键自动findViewById功能
在线网站工具 地址:http://android.lineten.net/layout.php
- 源码分析——迁移学习Inception V3网络重训练实现图片分类
1. 前言 近些年来,随着以卷积神经网络(CNN)为代表的深度学习在图像识别领域的突破,越来越多的图像识别算法不断涌现.在去年,我们初步成功尝试了图像识别在测试领域的应用:将网站样式错乱问题.无线领域 ...
- 洛谷 P3359 改造异或树
题目描述 给定一棵n 个点的树,每条边上都有一个权值.现在按顺序删掉所有的n-1条边,每删掉一条边询问当前有多少条路径满足路径上所有边权值异或和为0. 输入输出格式 输入格式: 第一行一个整数n. 接 ...
- QQ聊天窗口上的广告与QQ弹窗广告的完美屏蔽去除
涉及的软件 1. QQ (笔者的为v8.2版本) 2. Win7 3. ADSafe(3.13.308.9900正式版) 前言 QQ广告十分讨人厌,除了QQ弹窗的广告,让人十分反感外,最近发现QQ聊 ...
- lua 的一些常用概念
1 a={} //定义了一个table a a[10000]=1 //这里的table中只有一个元素,10000,而不是有10000个元素 2 x=math.pi //定义了x等于π print( ...
- Android 两种注册、发送广播的区别
前言:前面文章记录了Service的使用,这次来记录另一个四个组件之一的BroadcastReceiver.主要介绍两种发送和注册广播的区别. BroadcastReceiver广播接收者用于接收系统 ...
- js右下角弹窗代码(实测好用)
实测好用的js右下角弹窗代码 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "htt ...