为什么不推荐用index当做v-for中的key值呢?

前言

在Vue的学习中,我们经常听见或看见不推荐在v-for循环中使用index作为key的值,可是在写代码时,为了图方便我们还是经常使用,这样会带来什么后果呢,本篇文章从原理来一探究竟。

在说明为什么不能写:key="index"之前,我们还得先了解一些知识点。

虚拟DOM

虚拟DOM(Virtual DOM)是一种在前端开发中常用的概念,它是一个虚拟的内存数据结构,代表了真实DOM的层次结构。虚拟DOM通常是一种以 JS 对象树的形式表示真实的 DOM 结构,包含了DOM元素的类型、属性、子元素等信息。来个场景理解一下。

xml 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
  <div id="app">
    <ul class="list" id="list">
      <li class="item" v-for="{item, index} in list" :key="index">{{item}}</li>
    </ul>
  </div>
  
  <script>
    const { createApp, ref } = Vue

    createApp({
      setup() {
        const list = ref(['html', 'css', 'vue'])
        return {
          list
        }
      }
    }).mount('#app')
  </script>

首先Vue会通过compiler编译器将模版(template)代码生成为一个虚拟DOM结构

css 复制代码
let Dom = {  // 虚拟dom
  tagName: 'ul',
  props: {
    class: 'list',
    id: 'list'
  },
  children: [
    {
      tagName: 'li',
      props: {
        class: 'item'
      },
      children: ['html']
    },
    {
      tagName: 'li',
      props: {
        class: 'item'
      },
      children: ['css']
    },
    {
      tagName: 'li',
      props: {
        class: 'item'
      },
      children: ['js']
    },
  ]
}

然后compiler编译器继续工作,生成一个真正的html,交给浏览器去进行渲染(这个环节叫render)。 这是没有数据改变的情况,可当数据发生改变时会发生什么呢?

首先compiler编译器会再一次编译模版代码生成一个新的虚拟DOM结构,这时摆在编译器面前有两个选择了,一是重新渲染这个新的虚拟 DOM ,二是只更新新旧虚拟 DOM 之间改变的地方。大家都是聪明人,一眼就能看出第二种方式更加节省性能(比如可以避免回流重绘),这一步也正是发挥了大名鼎鼎的diff算法的作用。

Diff算法

Diff算法通常用于比较前后两次渲染的虚拟DOM树,找出它们之间的差异,生成一个补丁,并将这些差异应用到实际的DOM上,以更新用户界面。

Vue中使用的虚拟DOM Diff算法可以简述如下:

  1. Diff算法首先需要遍历两棵树的节点,将树的结构转换为一系列的操作指令。
  2. 进行同层比较,这个比较过程会根据节点的类型和key属性进行。如果新旧节点类型不相同,直接废弃老DOM
  3. 如果新旧节点的类型相同,则会比较它们的属性。如果有属性发生了变化,产生一个补丁包,用于更新对应的DOM属性。
  4. 如果节点的类型和key属性都相同,Diff算法会采用双端队列的方式递归比较它们的子节点,并且尽量复用。
  5. 同上。

为什么不能写:key="index"(重点来了,敲黑板🔥🔥🔥)

经过上面的铺垫,我们终于可以好好的讲解为什么不能用index当做key值了。

key是在 Vue 中用来标识虚拟 DOM 节点的特殊属性。它的主要作用是帮助 Vue 识别每个节点的唯一性,以便在进行虚拟 DOM 的更新时能够更加高效地重用和重新排序 DOM 元素。

来个场景理解一下。

xml 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
  <div id="app">
    <ul class="list" id="list">
      <li class="item" v-for="(item, index) in list" :key="index">{{item}}</li>
    </ul>
    <button @click="add">add</button>
  </div>

  <script>
    const { createApp, ref } = Vue

    createApp({
      setup() {
        const list = ref(['html', 'css', 'vue'])
        
        const add = () => {
          list.value.unshift('Ywis')
        }
        return {
          list,
          add
        }
      }
    }).mount('#app')
  </script>
</body>
</html>

在这个场景之中有一个 ul 列表和一个 按钮,每当我们按一下按钮时,就会向list数组开头添加一个新的元素('Ywis'), 这时就会发生compiler编译器重新编译、新旧虚拟DOM结构比较、浏览器重新渲染等一系列操作。

这样的变化会在浏览器上怎么体现呢?我们来看一看

我们会发现,每一次数据更改,列表都会重新渲染一遍。可是依据Diff算法'html', 'css', 'vue'这三项应该是会被复用的,像这样:

这就是用index作为key的弊端所在了,索引是基于数组的顺序,而不是基于每个项的内容。如果列表中的项目发生了变化,例如添加或删除了项目,这会导致相同的索引对应着不同的项,这样就会破坏Vue的虚拟DOM Diff算法,使得整个列表被浏览器重新渲染,性能大大的降低了。

结尾 🌸🌸🌸

一般来说,我们只做数据的渲染就可以使用index作为key值,并没有什么问题,但是涉及到数据的操作,比如添加或删除数据的时候就不要用了,毕竟尤雨溪和Vue团队为我们提供了这么强大的Diff算法

既然key的值要有唯一性,那么大家思考这样一个问题,我们可以用Math.random()作为key的值吗?

哈哈哈哈哈,当然是不可以的了,如果在组件的渲染中使用了随机数作为key,那么在每次重新渲染组件时,都会生成不同的key值,也会导致不必要的DOM重新渲染。

谢谢各位小伙伴愿意花宝贵的时间阅读这篇文章,希望我们可以共同成长!😜😜😜

相关推荐
你也向往长安城吗几秒前
推荐一个三维导航库:three-pathfinding-3d
javascript·算法
karrigan10 分钟前
async/await 的优雅外衣下:Generator 的核心原理与 JavaScript 执行引擎的精细管理
javascript
wycode18 分钟前
Vue2实践(3)之用component做一个动态表单(二)
前端·javascript·vue.js
wycode1 小时前
Vue2实践(2)之用component做一个动态表单(一)
前端·javascript·vue.js
第七种黄昏1 小时前
Vue3 中的 ref、模板引用和 defineExpose 详解
前端·javascript·vue.js
我是哈哈hh2 小时前
【Node.js】ECMAScript标准 以及 npm安装
开发语言·前端·javascript·node.js
张元清2 小时前
电商 Feeds 流缓存策略:Temu vs 拼多多的技术选择
前端·javascript·面试
pepedd8643 小时前
浅谈js拷贝问题-解决拷贝数据难题
前端·javascript·trae
@大迁世界3 小时前
useCallback 的陷阱:当 React Hooks 反而拖了后腿
前端·javascript·react.js·前端框架·ecmascript
小高0073 小时前
📌React 路由超详解(2025 版):从 0 到 1 再到 100,一篇彻底吃透
前端·javascript·react.js