跳转到内容

Diff 算法

刀刀
0字
0分钟
2026/8/17

React Diff算法原理图

pCF9Ouq.png

React Diff算法key的作用

虚拟 DOMkey 的作用:

  1. key 是虚拟 DOM 对象的标识,更新显示时 key 起着极其重要的作用。
  2. 当状态的数据发生变化时,react 会根据 【新数据】 生成 【新的虚拟 DOM】,随后 react 进行 【新虚拟 DOM】的 diff 算法比较,比较规则如下:
    • 旧虚拟 DOM 中找到与新虚拟 DOM 相同的 key
      1. 若虚拟 DOM 中内容没变,直接使用之前的真实 DOM
      2. 若虚拟 DOM 中内容改变,则生成新的真实 DOM,随后再替换掉页面中之前的真实 DOM
    • 旧虚拟 DOM 中未找到与新虚拟 DOM 相同的 key :根据数据创建新的真实 DOM,随后渲染到页面

React Diff算法key避免使用索引

  1. 若对数据进行添加、逆序删除等破坏顺序操作:会产生没必要的真实 DOM 更新,效率低
  2. 如果结构中包含输入类的 DOM :会产生错误的 DOM 更新

下面来看一个例子:有一个动态循环渲染数组的数据,点击按钮后在最开始添加一条新数据,此时页面会重新渲染了。分为两个 key ,一个索引,一个 id 。代码如下:

jsx
class Person extends React.Component {
  state = {
    persons: [
      { id: 1, name: '小张', age: 18 },
      { id: 2, name: '小李', age: 19 },
    ],
  }

  add = () => {
    const { persons } = this.state
    const p = { id: persons.length + 1, name: '小王', age: 20 }
    this.setState({ persons: [p, ...persons] })
  }

  render() {
    return (
      <div>
        <h2>展示人员信息</h2>
        <button onClick={this.add}>添加一个小王</button>
        <h3>使用index(索引值)作为key</h3>
        <ul>
          {this.state.persons.map((personObj, index) => {
            return (
              <li key={index}>
                {personObj.name}---{personObj.age}
                <input type="text" />
              </li>
            )
          })}
        </ul>
        <hr />
        <hr />
        <h3>使用id(数据的唯一标识)作为key</h3>
        <ul>
          {this.state.persons.map((personObj) => {
            return (
              <li key={personObj.id}>
                {personObj.name}---{personObj.age}
                <input type="text" />
              </li>
            )
          })}
        </ul>
      </div>
    )
  }
}

点击新增按钮时,它们各自做了以下操作:

  • index

    txt
    初始数据:
     {id:1,name:'小张',age:18},
     {id:2,name:'小李',age:19},
    初始的虚拟DOM:
     <li key=0>小张---18</li>
     <li key=1>小李---19</li>
    
    更新后的数据:
      {id:3,name:'小王',age:20},
      {id:1,name:'小张',age:18},
      {id:2,name:'小李',age:19},
    更新数据后的虚拟DOM:
      <li key=0>小王---20</li>
      <li key=1>小张---18</li>
      <li key=2>小李---19</li>

    此时新旧虚拟 DOM 对比,key 索引为 0 的数据不一致,生成真实 DOMkey 索引为 1 的数据不一致,生成真实 DOMkey 索引为 2 没有该旧虚拟 DOM ,是新数据,生成真实 DOM

    可以看到,虽然我们知道有两条数据是一致的,但是 diff 算法在对比时索引错乱导致他们对比都不相等,全部都要生成真实 DOM ,造成很多的性能浪费。

  • id

    此时新旧虚拟 DOM 对比,keyid 为 3 没有该旧虚拟 DOM ,是新数据,生成真实 DOMkeyid 为 1 对比后数据一致,不生成真实 DOM 直接复用; keyid 为 2 有该旧虚拟 DOM ,对比后数据一致,不生成真实 DOM 直接复用。

    这么看来,只需要生成一次真实 DOM ,如果数据多则极大提高性能效率。

同样的,如果循环体 li 标签内存在输入框 input ,则输入框节点对比后无变化,因此会复用,此时索引作为 key 时会出现下面这种 bug

pCFZfDH.png

⚠️ 注意

如果不存在对数据进行逆序添加、逆序删除等破坏顺序操作,仅用于渲染展示,使用索引是没有问题的。

贡献者

The avatar of contributor named as duyidao duyidao
The avatar of contributor named as 刀刀 刀刀

页面历史

刀刀博客累计访客 人;文档累计访问量共