浅析React与Vue中key为什么是必须的

385 阅读4分钟

vue官方文档

当 Vue 正在更新使用 v-for 渲染的元素列表时,它默认使用**“就地更新”**的策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每个元素,并且确保它们在每个索引位置正确渲染。

这个默认的模式是高效的,但是只适用于不依赖子组件状态或临时 DOM 状态 (例如:表单输入值) 的列表渲染输出

为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key attribute:

<div v-for="item in items" :key="item.id">
  <!-- content -->
</div>

提示

不要使用对象或数组之类的非基本类型值作为 v-for 的 key。请用字符串或数值类型的值。

react官方文档

key 帮助 React 识别哪些元素改变了,比如被添加或删除。因此你应当给数组中的每一个元素赋予一个确定的标识。

一个元素的 key 最好是这个元素在列表中拥有的一个独一无二的字符串。通常,我们使用数据中的 id 来作为元素的 key,当元素没有确定 id 的时候,万不得已你可以使用元素索引 index 作为 key

在默认条件下,当递归 DOM 节点的子元素时,React 会同时遍历两个子元素的列表;当产生差异时,生成一个 mutation。

在子元素列表末尾新增元素时,更变开销比较小。比如:

<ul>
  <li>first</li>
  <li>second</li>
</ul>

<ul>
  <li>first</li>
  <li>second</li>
  <li>third</li>
</ul>

React 会先匹配两个 <li>first</li> 对应的树,然后匹配第二个元素 <li>second</li> 对应的树,最后插入第三个元素的 <li>third</li> 树。

如果简单实现的话,那么在列表头部插入会很影响性能,那么更变开销会比较大。比如:

<ul>
  <li key="2015">Duke</li>
  <li key="2016">Villanova</li>
</ul>

<ul>
  <li key="2014">Connecticut</li>
  <li key="2015">Duke</li>
  <li key="2016">Villanova</li>
</ul>

现在 React 知道只有带着 '2014' key 的元素是新元素,带着 '2015' 以及 '2016' key 的元素仅仅移动了。

总结

一、key的作用是什么?

key是给每一个vnode的唯一id,可以依靠key,更准确, 更的拿到oldVnode中对应的vnode节点。

1. 更准确

因为带key就不是就地复用了,在sameNode函数 a.key === b.key对比中可以避免就地复用的情况。所以会更加准确。

2. 更快

利用key的唯一性生成map对象来获取对应节点,比遍历方式更快

cn.vuejs.org/v2/guide/li…)

二、用key和不用key到底有什么区别呢?

不用 key:
  • 就地复用节点。在比较新旧两个节点是否是同一个节点的过程中会判断成新旧两个节点是同一个节点,因为 a.key 和 b.key 都是 undefined。所以不会重新创建节点和删除节点,只会在节点的属性层面上进行比较和更新。所以可能在某种程度上(创建和删除节点方面)会有渲染性能上的提升;
  • 无法维持组件的状态。由于就地复用节点的关系,可能在维持组件状态方面会导致不可预知的错误,比如无法维持改组件的动画效果、开关等状态;
  • 也有可能会带来性能下降。因为是直接就地复用节点,如果修改的组件,需要复用的很多节点,顺序又和原来的完全不同的话,那么创建和删除的节点数量就会比带 key 的时候增加很多,性能就会有所下降;
用 key:
  • 维持组件的状态,保证组件的复用。因为有 key 唯一标识了组件,不会在每次比较新旧两个节点是否是同一个节点的时候直接判断为同一个节点,而是会继续在接下来的节点中找到 key 相同的节点去比较,能找到相同的 key 的话就复用节点,不能找到的话就增加或者删除节点。
  • 查找性能上的提升。有 key 的时候,会生成 hash,这样在查找的时候就是 hash 查找了,基本上就是 O(1) 的复杂度。
  • 节点复用带来的性能提升。因为有 key 唯一标识了组件,所以会尽可能多的对组件进行复用(尽管组件顺序不同),那么创建和删除节点数量就会变少,这方面的消耗就会下降,带来性能的提升。
性能提升不能只考虑一方面,不是 diff 快了性能就快,不是增删节点少了性能就快,不考虑量级的去评价性能,都只是泛泛而谈。

参考

木易杨前端博客

react官方文档

vue官方文档