十一、React学习笔记整理(DIFF算法中的Key)

116 阅读3分钟

经典面试题:

  1. react/vue中的key有什么作用?(key的内部原理是什么?)
  2. 为什么遍历列表时,key最好不要用index? 代码示范:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>hello</title>
</head>
<body>
    <div id="test"></div>
    <script src="https://cdn.staticfile.org/react/16.8.0/umd/react.development.js"></script>
    <script src="https://cdn.staticfile.org/react-dom/16.8.0/umd/react-dom.development.js"></script>
    <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>

    <script type="text/babel">
      class Person extends React.Component {

        state = {persons: [{id: 1, name: '张三', age: 17}, {id: 2, name: '赵六', age: 18},]}
        componentDidMount(){
          setInterval(()=>{
            this.setState({date: new Date()})
          }, 1000)
        }
        addPerson = () => {
          const {persons} = this.state
          let visible = persons.length +1
          this.setState({persons: [{id: visible, name: '路人甲'+ visible, age: visible}, ...persons]})
        }
        render() {
          const {persons} = this.state
          return (
            <div>
              <h1>人员列表</h1>
              <button onClick={this.addPerson}>添加一个人</button>
              <h5>使用index做key</h5>
              {
                persons.map((item, index)=> {
                  return <div key={index}>{item.name}-->{item.age}<input /></div>
                })
              }
              <hr/>
              <h5>使用唯一字段做key</h5>
              {
                persons.map((item, index)=> {
                  return <div key={item.id}>{item.name}-->{item.age}<input /></div>
                })
              }
            </div>
          );
        }
      }
        ReactDOM.render(<Person/>, document.getElementById('test'))
    </script>
</body>
</html>

代码运行后,向列表第一行位置添加一条人员信息,列表展示如下:

明显看到使用index作为key时,列表信息发生错乱。使用唯一字段作为key,则显示正常。

问题分析:
      慢动作回放: --使用index作为key
        初始化数据:
                  {id: 1, name: '张三', age: 17}
                  {id: 2, name: '赵六', age: 18}
        初始化数据虚拟DOM:
                 <div key=0>张三-->17</div>
                 <div key=1>赵六-->18</div>

        更新后的数据:
                  {id: 3, name: '路人甲', age: 19}
                  {id: 1, name: '张三', age: 17}
                  {id: 2, name: '赵六', age: 18}
        更新后数据虚拟DOM:
                 <div key=0>路人甲-->19<input /></div> (转换真实DOM,input没有做更改)
                 <div key=1>张三-->17<input /></div>   (转换真实DOM,input没有做更改)
                 <div key=2>赵六-->18<input /></div>   (转换真实DOM,input是新转的DOM)

      慢动作回放: --使用唯一标识作为key
           初始化数据:
               {id: 1, name: '张三', age: 17}
               {id: 2, name: '赵六', age: 18}
           初始化数据虚拟DOM:
               <div key=1>张三-->17</div>
               <div key=2>赵六-->18</div>

           更新后的数据:
               {id: 3, name: '路人甲', age: 19}
               {id: 1, name: '张三', age: 17}
               {id: 2, name: '赵六', age: 18}
           更新后数据虚拟DOM:
               <div key=3>路人甲-->19<input /></div> (转换真实DOM)
               <div key=1>张三-->17<input /></div>   (复用旧DOM)
               <div key=2>赵六-->18<input /></div>   (复用旧DOM)

总结

一. 虚拟DOM中key的作用

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

二.用index作为key可能会引发的问题

  1. 做对数据进行:逆序添加,逆序删除等破坏顺序的操作,会产生没必要的真实DOM更新,=>界面效果没问题,但效率低
  2. 如果结构中包含输入类的DOM,会产生错误DOM更新。=>界面会有问题
  3. 注意:如果不存在对数据的逆序添加、逆序删除等破坏顺序的操作,仅用于渲染列表展示,使用index作为key是没问题的

三.开发中如果选择key

  1. 最好使用每条数据的唯一标识的key,如id、手机号、身份证号、学号等唯一值
  2. 如果确定只是简单的展示数据,用index也是可以的