上周在处理项目的时候,由于之前项目中引用的是 cdn 中的生产环境的 React 所以导致所有在开发环境中应该暴露的 warnning 都被屏蔽了,上周修改了 webpack 的配置把 React 改为 develop 的时候,就出现了一堆下列的报错:
Warning: Each child in an array or iterator should have a unique "key" prop.
意思是: 数组或迭代器中的每个子元素都应该有一个唯一的“key”属性。
解决的方法和能见到,就是为数组中的元素传递一个唯一的key(例如list的唯一id),就可以很好地解决这个问题。由于这个是一个 warning ,很多同学在开发中可能会忽略或者是屏蔽调这样一个警告,那究竟加不加这个 key 属性会有什么不一样?它的作用又是什么。
当在数组或者迭代器中循环渲染元素的时候,其实是用到了 React 的 element diff 算法,,当节点处于同一层级时,React diff 提供了三种节点操作,分别为:INSERT_MARKUP(插入)、MOVE_EXISTING(移动)和 REMOVE_NODE(删除)。
举个例子,有以代码,
// props.dataLists = ['a', 'b', 'c', 'd'];
const dataList = props => {
<div>
props.dataLists.map(value=><div key={value}>{value}</div>)
</div>
}
当现在由外部传入的dataLists修改为 ['b','a','d','c']
的时候,此时,如果按照原始的diff算法,对比旧的props.dataLists = ['a', 'b', 'c', 'd'];
,发现 第一个位 b != a
,则创建并且插入 b 到新的集合里面,删除老得a(这里我们假设 abcd 也代表一个element)如此类推,创建插入了 a,d,c删除了b,c,d;
old: a, b, c, d
new: b, a, d, c
假设这里有10000个 elements, 这里的开销大到不能想象,而且仔细的你可能已经发现了,其实上面的 element并没有发生变化,他们仅仅是发生了位置的变化,但是却产生了非常大开销的删除、创建和删除操作,说白了,其实我们只要交换以下几个 element 的位置就好了。
所以,针对这样一个优化,React 提出了这样的优化策略。
允许开发者对同一层级的同组子节点,添加唯一 key 进行区分
新老集合所包含的节点,老集合进行 diff 差异化对比,通过 key 发现新老集合中的节点都是相同的节点,因此无需进行节点删除和创建,只需要将老集合中节点的位置进行移动,更新为新集合中节点的位置,此时 React 给出的 diff 结果为:b、d 不做任何操作,a、c进行移动操作,即可。
另外,看 Babel 转换 jsx 后,也很好理解为什么通过 key 可以分辨出 变化前后 element 的关系,为什么只有数组需要key。
// before babel
const dataList = props => {
<div>
<div>
<h1>hello world</h1>
{[
<div key={1}>1</div>,
<div key={2}>2</div>
]}
</div>
</div>
}
// afer babel
"use strict";
"use strict";
var dataList = function dataList(props) {
React.createElement(
"div",
null,
React.createElement(
"div",
null,
React.createElement("h1", null, "hello world"),
[
React.createElement(
"div",
{
key: 1
},
"1"
),
React.createElement(
"div",
{
key: 2
},
"2"
)
]
)
);
};
不管 props 的变化,数组外的每个元素失踪出现在 React.createElement 参数列表中的固定位置不变,这个位置就是天然的 key。
另外我也发现,当使用 react-router
的时候,通常 route 和 redirect 也要给 key 赋值。
参考资料:
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。