React 基础小知识:Diffing 算法

2,035 阅读3分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。

验证 Diffing 算法

class Time extends React.Component {
  state = {date: new Date()}
  componentDidMount() {
    setInterval(() => {
      this.setState({date: new Date()})
    }, 1000)
  }
  render() {
    return (
      <div>
        <h1>hello</h1>
        <input type="text"/>
        <span>现在是:{this.state.date.toTimeString()}</span>
      </div>
    )
  }
}
ReactDOM.render(<Time/>, document.getElementById("test"));

截屏2021-10-14 下午8.45.19.png

由上述案例可以看出,span 标签里面的时间每隔一秒就进行更新,可以通过在 input 标签里输入的方式证明 input 标签并为随着 span 标签更新而更新。因此:Diffing 算法对比的最小粒度是节点

遍历列表时的 key

经典面试题:

  1. React/Vue 中的 key 有什么作用?key 内部原理是什么?
  2. 为什么遍历列表时,key 最好不要用 index?
/**
【使用 index 索引值 作为 key】
初始数据:
{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>

由于 index 不同,导致小张、小李都没有被复用,效率变低。
----------------------------------------------
【使用 id 唯一标识做为 key】
初始数据:
{id: 1, name: '小张', age: 18}
{id: 2, name: '小李', age: 19}

初始的虚拟 DOM:
<li key=1>小张 --- 18</li>
<li key=2>小李 --- 19</li>

更新后的数据:
{id: 3, name: '小王', age: 20}
{id: 1, name: '小张', age: 18}
{id: 2, name: '小李', age: 19}

更新数据后的虚拟 DOM:
<li key=3>小王 --- 20</li>
<li key=1>小张 --- 18</li>
<li key=2>小李 --- 19</li>

小张、小李被复用,效率提高。
**/
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>
        <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>
    );
  }
}
ReactDOM.render(<Person />, document.getElementById("test"));

截屏2021-10-14 下午9.34.02.png

  1. 虚拟 DOM 中 key 的作用:
  • 简单地说:key 是虚拟 DOM 对象的标识,在更新显示时起着极其重要的作用。
  • 详细地说:当状态中的数据发生变化时,React 会根据新数据生成新的虚拟 DOM,随后 React 进行新虚拟 DOM旧虚拟 DOM 进行 Diff 比较,规则如下:
    • 旧虚拟 DOM找到了新虚拟 DOM 相同的 key:
      1. 虚拟 DOM 中内容没变,直接使用之前的真实 DOM
      2. 虚拟 DOM 中内容变了,则生成新的真实 DOM,随后替换掉页面中之前的真实 DOM
    • 旧虚拟 DOM未找到新虚拟 DOM 相同的 key:
      1. 根据数据创建新的真实 DOM,随后渲染到页面。
  1. 用 index 作为 key 可能会引发的问题:
  • 若对数据进行:逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实 DOM 更新 ---> 界面效果没问题但效率低。
  • 如果结构中还包含输入类 DOM:会产生错误 DOM 更新 ---> 界面有问题
  • 注意:如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用 index 作为 key 时没有问题的。
  1. 开发中如何选择 key:
  • 最好使用每条数据的唯一标识作为 key,比如 id、手机号、身份证号、学号等唯一值。
  • 如果确定只是简单的展示数据,用 index 也是可以的。