集帅(美)们,别再写 :key = "index" 啦!

浅聊一下

灵魂拷问:你有没有在v-for里使用过:key = "index",如果有,我希望你马上改正过来并且给我点个赞,如果没有,来都来了,顺手给我点个赞...

假如您也和我一样,在准备春招。欢迎加我微信shunwuyu,这里有几十位一心去大厂的友友可以相互鼓励,分享信息,模拟面试,共读源码,齐刷算法,手撕面经。来吧,友友们!

开始

在向掘友们解释为什么不能使用 :key = "index" 之前,我想我还得向你们铺垫一点东西

虚拟DOM

什么是虚拟DOM呢?虚拟DOM是一个对象,没想到吧...我们来看看Vue是如何将template模板里面的东西交给浏览器来渲染的

首先通过 compiler 将 template模板变成一个虚拟DOM,再将虚拟DOM转换成HTML,最后再交给浏览器V8引擎渲染,那么虚拟DOM是什么样的呢?

html 复制代码
<!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 id="item">
           <li v-for="item in list" class="item">{{item}}</li>
       </ul>
    </div>
    <script>
        const { createApp, ref } = Vue
        createApp({
            setup() {
               const list = ref(['vue','js','html'])
               return {
                    list
                }
            }
        }).mount('#app')
    </script>
</body>

</html>

在这里,template模板实际上是

html 复制代码
 <ul>
      <li v-for="item in list">{{item}}</li>
 </ul>

通过v-for循环,渲染出来了3个li

html 复制代码
<ul>
    <li>vue<li>
    <li>js<li>
    <li>html<li>
</ul>

我们的compiler会将这个模板转化成虚拟DOM

js 复制代码
let oldDom = {
    tagName = 'ul',
    props:{
        //存放id 和 class 等
        id:'item'
    },
    children[
        {
            tagName = 'li',
            props:{
                class:'item'
            },
            children:['vue']
        },
                {
            tagName = 'li',
            props:{
                class:'item'
            },
            children:['js']
        },
                {
            tagName = 'li',
            props:{
                class:'item'
            },
            children:['html']
        },
    ]
}

diff算法

给前面的例子来点刺激的,加上一个按钮和反转函数,点击按钮,list反转

html 复制代码
<!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>
           <li v-for="item in list">{{item}}</li>
       </ul>
       <button @click="change">change</button>
    </div>
    <script>
        const { createApp, ref } = Vue
        createApp({
            setup() {
               const list = ref(['唱','跳','rap','篮球'])
               const change = ()=>{
                list.value.reverse()
               }
               const add = ()=>{
                list.value.unshift('6')
               }
               return {
                    list,
                    change,
                }
            }
        }).mount('#app')
    </script>
</body>

</html>

点击change按钮,此时我们的DOM更改vue又是如何来更新DOM的呢?

众所周知,回流和重绘会消耗极大的性能,而当DOM发生变更的时候会触发回流重绘(可以去看我的文章(从输入4399.com到页面渲染之间的回流和重绘),那么vue3就有一个diff算法,用来优化性能

当DOM更改,compiler会生成一个新的虚拟DOM,然后通过diff算法来生成一个补丁包,用来记录旧DOM和新DOM的差异,然后再拿到html里面进行修改,最后再交给浏览器V8进行渲染

简单介绍一下diff算法的比较规则

  1. 同层比较,是不是相同的结点,不相同直接废弃老DOM
  2. 是相同结点,比较结点上的属性,产生一个补丁包
  3. 继续比较下一层的子节点,采用双端对列的方式,尽量复用,产生一个补丁包
  4. 同上

别再写 :key = "index"

要说别写 :key = "index" ,我们得先明白key是用来干什么的...如果没有key,那么在diff算法对新旧虚拟DOM进行比较的时候就没法比较了,你看这里有两个一样的vue,当反转顺序以后diff算法不知道哪个vue该对应哪个vue了

如果我们用index来充当key的话来看,当我们在头部再插入一个结点的时候,后面的index其实是改变了的,导致diff算法在比较的时候认为他们与原虚拟DM都不相同,那么diff算法就等于没有用...

可以用随机数吗?

js 复制代码
<li v-for="item in list" :key="Math.random()">

想出这种办法的,也是一个狠人...当然是不行的,因为在template模板更新时,会产生一个新的虚拟DOM,而这个虚拟DOM里面的key也是随机值,和原虚拟DOM里的key99.99999%是不一样的...

结尾

希望你以后再也不会写 :key = "index" 了

假如您也和我一样,在准备春招。欢迎加我微信shunwuyu,这里有几十位一心去大厂的友友可以相互鼓励,分享信息,模拟面试,共读源码,齐刷算法,手撕面经。来吧,友友们!

相关推荐
高级程序源几秒前
SSM仓储管理系统31582-计算机课程设计、毕业设计
vue.js·python·mysql·小程序·flask·课程设计
骑着蜗牛撵大象32722 分钟前
豆包 Seed 驱动的记账智能体:从拍票抽取到月报生成的全链路落地记录
java·前端·javascript·结构化输出·豆包seed·多模态识别·记账系统
溪语流沙23 分钟前
【Web全栈进阶】前端世界观:现代工具链与框架为什么存在
前端·javascript·vue.js
kyriewen1 小时前
我逐个测了 JSON.stringify 的 7 个坑:第 1 个就在丢数据
前端·javascript·程序员
积流成海1 小时前
用代码实现哈希计算器:Web Crypto API 实战
前端·算法·哈希算法·哈希函数·盘子工具站
data analyse 4561 小时前
服务端访问日志聚合 PV 与 UV 怎么实现?日志采集与口径设计
大数据·前端·数据分析
JavaGuide1 小时前
DeepSeek Harness 重磅更新,Codex、Claude Code 子智能体进官方插件列表了,还有 Git 工作树和思考翻译!
前端·后端
WeiXin_DZbishe2 小时前
基于微信小程序的本地闲置物品交易平台-计算机毕设 附源码【课程设计】26825
vue.js·spring boot·python·微信小程序·架构·php·课程设计
小羊没烦恼!2 小时前
OS X 上使用.NET开发应用程序
开发语言·前端·数据库·人工智能·c#·策略模式
高级程序源2 小时前
springboot本地生活服务平台33399-计算机课程设计、毕业设计
vue.js·spring boot·python·mysql·小程序·flask·课程设计