React/Vue中的key有什么作用?为什么遍历列表时,key最好不要用index?

前端开发中,只要涉及到列表渲染,那么无论是Angular、React还是Vue框架,都会提示或要求每个列表项使用唯一的key,那很多开发者就会直接使用一个唯一的id或数组的index作为key的值,而并不知道使用key的原理。那么这篇博客就会讲解key的作用以及为什么最好不要使用index作为key的属性值。

文章目录

      • 1.key的作用
        • 1.1举例说明
      • 2.key最好不要使用数组的index
        • 2.1 举例说明

1.key的作用

key之所以那么重要,是因为React/Vue框架使用了虚拟DOM和Diff算法高效率的更新视图。

虚拟DOM中key的作用:(使用React来讲解,Vue同理)

				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,随后渲染到到页面
1.1举例说明

需求:

有一个页面需要展示人员的信息,同时有个“添加”按钮,可以给列表添加一个人员,注意:是往列表前面追加一个人,即新添加的人展示在前面。

代码:

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]})//新追加的小王p是处于persons的第一位(涉及到数组解构知识)
		}

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

	ReactDOM.render(<Person/>,document.getElementById('test'))

React/Vue中的key有什么作用?为什么遍历列表时,key最好不要用index?_第1张图片
解释:我们应该知道React/Vue页面在数据发生改变时,是通过对比新、旧的虚拟DOM来更新页面展示的真实DOM的。

虚拟DOM就是一个普通的JS对象,下面我们就用一个对象来表示虚拟DOM

React/Vue中的key有什么作用?为什么遍历列表时,key最好不要用index?_第2张图片

Diff算法的对话:(新、旧分别代表新旧的虚拟DOM)

新:你那里有个key为3的东西吗?
旧:没有这玩意。
新:好吧,那我就新建一个key为3,内容为“小王- - - 20”的真实DOM
新:那你有个key为1的东西吗?
旧:有啊。
新:那它的内容是“小张- - - 18”吗?
旧:是的。
新:那我就不用创建新的真实DOM了,复用上次创建的那个真实DOM就可以。
新:那你有个key为2的东西吗?
旧:有啊。
新:那它的内容是“小李- - - 19”吗?
旧:是的。
新:那我也复用上次创建的真实DOM就行。这次工作真轻松,只需新建一个真实DOM,如果没有key和Diff算法这玩意,还使用原生JS的更新视图,那我就需要重新创建三个真实DOM了

综上对话,key是使得Diff算法高效对比新旧虚拟DOM的差异进而高效更新视图的重要一环。

2.key最好不要使用数组的index

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

							1. 若对数据进行:逆序添加、逆序删除等破坏顺序操作:
											会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。

							2. 如果结构中还包含输入类的DOM:
											会产生错误DOM更新 ==> 界面有问题。
											
							3. 注意!如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,
								仅用于渲染列表用于展示,使用index作为key是没有问题的。
2.1 举例说明

使用index作为key的属性值,在页面效果上没有任何差异,有差异的是效率.

还是上面的代码:

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}
                    </li>
                  )
                })}
              </ul>
            </div>
          )
        }
      }

      ReactDOM.render(<Person />, document.getElementById('test'))

React/Vue中的key有什么作用?为什么遍历列表时,key最好不要用index?_第3张图片

注意对比使用index作为key的属性值,生成的虚拟DOM的差别。

Diff算法的对话:

新:你那里有个key为0的东西吗?
旧:有啊。
新:那它的内容是“小王- - -20”吗?
旧:不是。
新:那内容改变了,我需要生成一个新的真实DOM替换上次展示在页面的真实DOM.
新:你那里有个key为1的东西吗?
旧:有啊。
新:那它的内容是“小张- - -18”吗?
旧:不是。
新:那内容改变了,我又要创建一个新的真实DOM去替换旧的DOM.
新:你那里有个key为2的东西吗?
旧:没有。
新:那我就新创建一个真实DOM咯,更新页面视图。害,这次我竟然要创建三个真实DOM,上次创建的真实DOM竟然一个都不能复用,累死我了

这就是使用index作为key的属性值的存在的副作用,但这种副作用不存在只向数组末尾追加数据的情况下。

比如向数组末尾追加数据展示:

React/Vue中的key有什么作用?为什么遍历列表时,key最好不要用index?_第4张图片
Diff算法会发现key为0和1对应的真实DOM是可以复用的,因此也是只需创建最后新加数据的一个真实DOM。

因此当新数据不是在末尾追加进数组,那么key就不要使用index降低效率了。

你可能感兴趣的:(React,vue,vue.js,reactjs,前端)