【React总结(一)】浅谈 React 中 key

上周在处理项目的时候,由于之前项目中引用的是 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 的 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 赋值

参考资料:

React 源码剖析系列 - 不可思议的 react diff

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

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

编辑于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区