原文:为什么react,vue要在列表组件中写key - 每天一个JavaScript小知识@Js中文网 · 码农进阶题库

原文地址:https://www.javascriptc.com/interview-tips/zh_cn/vue/react-of-vue-key/

题目描述:

写 React / Vue 项目时为什么要在列表组件中写 key,其作用是什么?

解题:

  • 思路一:

  • 主要是为了提升diff【同级比较】的效率。自己想一下自己要实现前后列表的diff,如果对列表的每一项增加一个key,即唯一索引,那就可以很清楚的知道两个列表谁少了谁没变。
  • 而如果不加key的话,就只能一个个对比了。

  • 思路二:

  • key能提高diff效率其实是不准确的。
  • 在不带key的情况下,判断sameVnode时因为a.key和b.key都是undefined,对于

    列表渲染

  • 来说已经可以判断为相同节点然后调用patchVnode了,实际根本不会进入到答主给的else代码,也就无从谈起“带key比不带key时diff算法更高效”了。

  • 然后,官网推荐推荐的使用key,应该理解为“使用唯一id作为key”。因为index作为key,和不带key的效果是一样的。
  • index作为key时,每个列表项的index在变更前后也是一样的,都是直接判断为sameVnode然后复用。

  • 说到底,key的作用就是更新组件时判断两个节点是否相同。相同就复用,不相同就删除旧的创建新的。
  • 正是因为带唯一key时每次更新都不能找到可复用的节点,不但要销毁和创建vnode,在DOM里添加移除节点对性能的影响更大。
  • 所以会才说“不带key可能性能更好”。看下面这个实验,渲染10w列表项,带唯一key与不带key的时间对比:

  • 不使用key的情况: ```javascript
  • ![https://static.javascriptc.com/imgs/day-questions/frontend/1.png](https://static.javascriptc.com/imgs/day-questions/frontend/1.png)
    + 使用id作为key的情况:
    ```javascript
    <li v-for="item in list" :key="item.id"></li>
    

    https://static.javascriptc.com/imgs/day-questions/frontend/2.png

    • list构造:
      const list1 = []
      const list2 = []
      for (let i = 0; i <= 100000; i++) {
        list1.push({
            id: i,
            text: i
        })
        list2.push({
            id: i * 2,
            name: 100000 - i
        })
      }
      
    • 因为不带key时节点能够复用,省去了销毁/创建组件的开销,同时只需要修改DOM文本内容而不是移除/添加节点,这就是文档中所说的“刻意依赖默认行为以获取性能上的提升”。

    • 既然如此,为什么还要建议带key呢?因为这种模式只适用于渲染简单的无状态组件。对于大多数场景来说,列表组件都有自己的状态。

    • 举个例子:一个新闻列表,可点击列表项来将其标记为”已访问”,可通过tab切换“娱乐新闻”或是“社会新闻”。

    • 不带key属性的情况下,在“娱乐新闻”下选中第二项然后切换到“社会新闻”,”社会新闻”里的第二项也会是被选中的状态,因为这里复用了组件,保留了之前的状态。要解决这个问题,可以为列表项带上新闻id作为唯一key,那么每次渲染列表时都会完全替换所有组件,使其拥有正确状态。

    • 这只是个简单的例子,实际应用会更复杂。带上唯一key虽然会增加开销,但是对于用户来说基本感受不到差距,而且能保证组件状态正确,这应该就是为什么推荐使用唯一id作为key的原因。至于具体怎么使用,就要根据实际情况来选择了。

    • 思路三:

    • vue和react虽然都采用了diff算法。 但是react本身的设计和vue的设计是截然不同的, vue采用了更加细粒度的更新组件的方式,即给每一个属性绑定监听, 而react是采用自顶而下的更新策略,每次小的改动都会生成一个全新的vdom。从而进行diff,如果不写key,可能就会发生本来应该更新却没有更新的bug。

    • 这个bug其实和diff算法有关,react团队完全可以写一个没有这个“bug”版本的代码, 但是这是一种权衡,一种性能和方便使用的权衡。 写不写key能够提高性能的根本在于一方面diff算法会优先判断key是否相同,如果相同则不进行后面的运算。 如果key相同,就更好了,根本不需要重新创建节点

    • 总结, 更确切的说应该是diff算法在你的复杂的列表稳定的时候能够明显提高性能,因为节点可以重用。

    • 但是对于列表频繁更新的场景, 节点不能重用,但是diff 可以省略一部分逻辑,因此性能也会更好。 但是两者的性能优化不在同一个纬度,一个是 创建和更新节点(我称之为渲染器)的优化, 一个是DOM diff 算法(我称之为核心引擎)的优化

    扩展阅读: