渲染列表时,React的差异比较算法需要列表范围内的唯一键来提高其性能(通常用于知道哪个列表项发生了变化)。这个唯一的密钥需要手动提供。React官方建议在呈现列表项时,使用列表数据中可用于唯一标识的字段作为关键字。如果没有,您可以不情愿地使用数组的索引,性能可能会受到影响。
一个例子
有这样一个场景,如下图所示,有一组动态输入,可以添加、删除、重新排序。数组元素生成的组件使用index作为键值,例如,下图中生成的ui显示:
上例中输入组件呈现的代码如下。有关所有完整代码,请参考==完整代码。
{这个。state.data.map ((v,idx)=item key={ idx } v={ v }/)}//item component render方法render () {return Li {this。props.v}输入类型=' text '/Li }首先,如果数组内容在页面中,
但是,动态数组呈现的组件会有问题。从上图也可以看出问题:数组动态变化后,页面上输入的输入内容与对应的数组元素顺序不对应。
为什么会这样?这篇文章将在后面解释。React初学者可能对此比较困惑。本文将与您讨论react的主要用法。
react键概述
钥匙的作用
react中的key属性是一个特殊的属性,它的出现并不是为了开发者(例如,为组件设置key后就不能得到它的关键道具),而是为了react本身。
那么反应如何使用密钥呢?《反应》的作者之一保罗奥尚尼斯提到:
关键不在于性能,而在于身份(这反过来会带来更好的性能)。随机分配和变化的值不会形成一个标识
简单来说,react是用钥匙来识别部件的,这是一种识别,就像我们的身份证是用来识别一个人一样。每个键对应一个组件,同一个键react被认为是同一个组件,因此不会创建与同一个键对应的后续组件。例如,以下代码:
//this . state . users content this . state={ users :[{ id :1,名3360 '张三' },{id:2,名: '李四' },{id3360 2,名: '王五' }],//省略}render()返回(div h3用户列表/H3 {this。state . users . map(u=div key={ u . id } { u . id } : { u . name }/div)}/div);上述代码在dom中渲染挂载后,用户列表中只有张三和李四两个用户,王五没有显示处理,主要是因为react根据key认为李四和王五是同一个组件,导致第一个被渲染,后续的被丢弃。
这样,利用密钥属性,可以与组件建立对应关系,react可以根据密钥决定是销毁重新创建的组件还是更新组件。
键是一样的,如果组件属性发生变化,react只更新组件的对应属性;没有变化,没有更新。如果键值不同,react将首先销毁组件(有状态组件的componentwillumunt将执行),然后重新创建组件(有状态组件的构造函数和componentwillumunt都将执行)。此外,应该指出
密钥不是用来提高react的性能的,但是很好地使用密钥有助于提高性能。
密钥的使用场景
在项目开发中,键属性最常用于数组动态创建子组件的情况,因此需要为每个子组件添加唯一的键属性值。
那么,为什么由数组动态创建的组件必须使用key属性呢?这与数组元素的动态特性有关。
以上面的用户列表为例,看看babel对上面代码的转换:
//转换前const element=(div h3用户列表/h3 {[div key={1}1:张三/div,div key={2}2:张四/div]}/div);//“转换后使用strict”;varelement=react.createelement ('div ',null,react.createelement ('H3 ',null,'用户列表'),[react.createelement ('div ',{key: 1},' 1:张三张'), react.createelement(' div ')根据babel转换后react.createelement中的代码,其他元素不需要key的原因是这些元素总是占据react . create element的固定位置,不管组件的状态或道具如何变化,这个位置就是自然键。
但是,当数组创建的组件可能由于动态操作而被重新呈现时,子组件的位置会发生变化。例如,在上述用户列表子组件中添加了一个新用户,上述两个用户的位置可能会发生如下变化:
var element=react.createelement ('div ',null,react.createelement ('H3 ',null,'用户列表'),[react.createelement ('div ',{key: 3},' 1:王武'), react.createelement ('div ',{key: 1},' 2:张三'), react . create element(' div ',{ key: 2 },' 3:李思')];可以看出,数组创建子组件的位置不是固定的,而是动态变化的;有了键属性,react可以根据键值判断是否是同一个组件。
此外,还有一个比较常见的场景:在逻辑复杂繁琐的组件中添加一个键后,后续操作可以改变组件的键属性值,从而先破坏前一个组件,再重新创建。
关键业务的最佳实践
如上所述,由数组创建的子组件必须具有关键属性,否则您可能会看到以下警告:
警告:数组或迭代器中的每个子元素都应该有一个唯一的“键”属性。检查“服务信息”的渲染方法。有关更多信息,请参见https://fb.me/react-warning-keys。
也许你会发现,这只是警告,不是错误,也不是强制性的。为什么react不需要按键并报告错误?其实是强制的,但是react已经按照要求默认为我们做了。它以数组的索引为键。
索引作为关键字是一种反模式
在列表数组中,使用key标识数组创建子组件时,如果数组的内容只显示为纯,不涉及数组的动态变化,其实可以使用index作为key。
但是,如果涉及数组的动态变化,例如向数组中添加元素、删除元素或重新排序元素,那么索引作为键将导致显示错误的数据。本文开头介绍的例子就是最好的证明。
{这个。state.data.map ((v,idx)=item key={ idx } v={ v }/)}//开头:['a ',' b ',C']=ul likey=' 0' a输入类型=' text '/Li likey=' 1 ' b输入类型=' text '/Li likey=' 2 ' C输入类型=' text '/Li/ul//数组重排-['c ',' b ',A']=ul likey=' 0' c输入类型=' text '/Li likey='键=0的元素说明了具体的更新过程。数组重新排序后:
组件重新呈现以获得新的虚拟dom;新老虚拟DOM不同,新老版本都有key=0的组件。如果react认为是同一个组件,那就只有更新组件的可能;然后对比他们的孩子,发现内容的文字内容是不同的(从a - c),而输入的成分没有变化。此时,组件的componentWillReceiveProps方法被触发,更新其子组件的文本内容;因为组件的子组件中的输入组件没有改变,并且它与父组件传入的任何道具都没有关联,所以输入组件不会被更新(也就是说,它的componentWillReceiveProps方法不会被执行),导致用户输入的值不会改变。这就是索引作为键的问题,所以不要使用索引作为键。
键值应该是稳定且唯一的
数组中生成的每一项都必须有一个key属性,key的值是一个永久唯一的值,即稳定唯一。
理想情况下,当循环对象数组时,数组中的每个项都有一个键值来区分其他项,这相当于数据库中的主键。这样,属性值可以用作键值。但一般来说,可能没有这样的属性值,需要我们自己去保证。
但需要指出的是,我们在保证数组中每一项唯一标识符的同时,还需要保证其值的稳定性,不能频繁更改。例如,以下代码:
{这个。state . data . map(El=MyComponent key={ Math.random()}/)}上面代码中my component的键值是由math . random随机生成的,虽然可以保持其唯一性,但是它的值是随机的而不是稳定的。当数组动态变化时,会导致数组元素中的每一项都被销毁,然后重新创建,有一定的性能开销。此外,它可能会导致一些意想不到的问题。所以:
key的值要稳定唯一,不能用random生成key的值。
因此,当无法使用random随机生成密钥时,我们可以使用全局localCounter变量添加一个稳定且唯一的密钥值,如下所示。
var local counter=1;this . data . foreach(El={ El . id=local counter;});//函数创建用户(user){ return }的其他注意事项.用户,id:本地计数器}}动态向数组添加元素时的键
当然,除了给数据元素生成的组件添加密钥,并且密钥应该是稳定和唯一的之外,还应该注意以下几点:
键属性被添加到自定义子组件,而不是子组件内部的顶级组件。
//mycomponent.render () {//errordiv键={{item。key}} {{item。名称}}/div}.//rightmycomponent键={{item。key } }/key值的唯一性有一个范围,即在数组生成的同级别同类型的组件上应该是唯一的,而不是全部。
Key不仅可以用在数组生成组件中,还可以用在其他地方,主要是因为react使用key来区分组件。同一个键表示同一个组件,react不会再破坏和创建组件实例,只会更新它们;关键是不同的,react将破坏现有的组件实例并重新创建新的组件实例。
{ this.state.type?div Son_1/Son_2//div : div son _ 2/Son_1//div }例如,在上面的代码中,当this.state.type的值发生变化时,Son _ 1和Son _ 2组件的原始实例将被销毁,而Son _ 1和Son _ 2组件的新实例将被重新创建,它们不能继承原始状态。事实上,他们只交换位置。为了避免这个问题,我们可以给组件添加一个键。
{ this.state.type?Div Son_1键=' 1'/son _ 2键=' 2 '/div : div Son _ 2键=' 2'/son _ 1键=' 1 '/div }这样,当this.state.type的值改变时,Son_1和Son2组件的实例不会被重新创建,react只是替换它们
以上就是本文的全部内容。希望对大家的学习有帮助,支持我们。