vue的就地更新与v-for的key属性

news2024/11/25 12:55:04

vue的就地更新

Vue中的就地更新到底是怎么回事,为什么会存在就地更新的现象?

注意下面的例子,使用v-for指令时,没有绑定key值,才有就地更新的现象,因为Vue默认按照就地更新的策略来更新v-for渲染的元素列表

下面的例子很简单,就是循环遍历数据list,当li标签被点击的时候就进行删除操作。现在我们关心的并不是如何实现功能,而是点击删除按钮时,整个ElementsDOM结构树是如何变化的。
按照常理来说,如果我们点击第一个li标签的删除按钮,那么就应该删除第一个li标签,点击第二个删除按钮,就删除第二个li标签,依次类推。但实际上Vue并不是这样做的,那Vue是怎么做的呢?
比如说,我现在点击第二个li标签的删除按钮,第二个li标签的内容是item-2。实际上Vue是先将第三个li标签的内容item-3,移动到item-2<span></span>标签内部,然后再删除内容是item-3li标签。换句话说,就是点击第二个删除按钮,删除的并不是第二个li标签,其实删除的是第三个li标签,Vue只是将第三个li标签中<span></span>标签的内容item-3,移动到第二个li标签中的<span></span>标签内部。

const App = {
  data() {
    return {
      list: [
        {
          id: 1,
          value: 'item-1'
        },
        {
          id: 2,
          value: 'item-2'
        },
        {
          id: 3,
          value: 'item-3'
        }
      ]
    }
  },
  template:`
    <ul>
      <li 
        v-for="(item, index) of list"
      >
        <span>{{ item.value }}</span>
        <button  @click="handleClickDel(index)">Delete</button>
      </li>
    </ul>
  `,
  methods: {
    handleClickDel(index) {
      this.list.splice(index,1);
    }
  }
}

没有点击item-2时的dom结构

image.png

点击item-2的delete按钮时,dom树的变化

点击item-2Delete按钮时,我们发现DOM结构树发生了更改。注意Elements背景色是紫色的标签,背景是紫色说明标签内容发生了变化。那么我们之前说,Vue此时删除的并不是第二个li标签,只是将item-3字符串移动到第二个li标签中的<span></span>标签中,删除第三个li标签。所以我们从图中可以看到,第二个li标签内容发生变化,而第三个li标签被删除。这种现象就称为就地更新。
image.png

就地更新的优势

上面叙述了就地更新的现象,那为什么Vue中存在就地更新的现象呢?它的优势又是什么呢?我们再来看一个例子吧。
我们首先看模板template中的两个<div></div>标签有什么共同点呢?两个<div></div>标签中都拥有标签<a href="#"></a>,按照常理来说,如果变量isLogin返回的是Truthy(真值)的时候,那么第一个<div></div>标签内部的span、a标签都会被渲染;当isLogin返回的是Falsy(虚值)的时候,那么第二个<div></div>标签内部的两个a标签也都会被渲染。
但是实际上并不是这样滴,Vue拥有就地更新的现象。所以点击<a href="javascript:;" @click="isLogin = true">登录</a>的时候,Vue会将<a href="#">注册</a>标签内部的字符串注册文字改为xiaoming,对这个<a href="#"></a>标签进行复用🤔,而不是重新渲染一个新的DOM元素。
我们通过这个例子就能看出Vue中就地更新的优势,首先就地更新是非常高效的,能够复用其它的DOM元素,减少DOM操作,减少DOM元素的重绘与回流,减少浏览器的性能消耗。

const App = {
  data() {
    return {
      isLogin: false
    }
  },
  template:`
    <div
      v-if="isLogin"
    >
      <span>欢迎您~</span>
      <a href="#"> xiaoming </a>
    </div>  
    <div v-else>
      <a href="javascript:;" @click="isLogin = true">登录</a>
      <a href="#">注册</a>
    </div>
  `
}

就地更新的问题

默认模式是高效的,但是只适用于列表渲染输出的结果不依赖子组件或者临时的DOM状态(例如表单输入值)的情况。
我们如何理解“**只适用于列表渲染输出的结果不依赖子组件或者临时的DOM状态(例如表单输入值)的情况”**这句话呢?我们来通过下面的例子进行解释。

就地更新遇到临时的dom状态

下面的例子中,(图1)是DOM结构树初始化的状态。当我手动往input框输入值后,点击item-2Detele按钮,此时会出现(图2)的状态,我们发现Vue的的确确是按照就地更新的策略进行的渲染列表。但是我们输入框输入的值并没有进行更新。是因为我手动输入值的状态是临时DOM状态,Vue没有办法判断节点内部这个临时DOM状态有什么用,因此Vue并不去会跟踪这个状态。所以就地更新遇见临时DOM状态就会出现(图2)中的问题。

const App = {
  el:'#app',
  data () {
    return {
      list: [
        {
          id: 1,
          value: 'item-1'
        },
        {
          id: 2,
          value: 'item-2'
        },
        {
          id: 3,
          value: 'item-3'
        }
      ]
    }
  },
  template:`
    <ul>
      <li 
        v-for="(item, index) of list" 
        :key="item.id">
          <span> {{ item.value }} </span>
          <input type="text" />
          <button @click="deleteItem(index)">DELETE</button>
      </li>
    </ul>
  `,
  methods: {
    deleteItem(index) {
      this.list.splice(index,1);
    } 
  }
}

(图1)
image.png
(图2)
image.png
当然上面例子相当于这样的写法,此时tempArr变量是固定的,是不变化的;因为tempArr是不变化的,那么input标签内部绑定的value值是没有办法更新的,而碰上Vue当中的就地更新的策略就出现(图2)中同样的问题。

const App = {
  data() {
    return {
      tempArr: [1, 2, 3],
      list:[
        {
          id: 1,
          value: 'item-1'
        },
        {
          id: 2,
          value: 'item-2'
        },
        {
          id: 3,
          value: 'item-3'
        }
      ]
    }
  },
  template: `
  <div>
    <ul>
      <li v-for="(item,index) of list">
        <span> {{ item.value }} </span>
        <input type="text" :value="tempArr[index]" />
        <button @click="deleteItem(index)">DELETE</button>
      </li>
    </ul>
  </div>
  `,
  methods: {
    deleteItem(index) {
      this.list.splice(index, 1);
    }
  }
}

就地更新遇到子组件

Vue文档中指出就地更新**只适用于列表渲染输出的结果不依赖子组件,**也就是说你模板中列表渲染的结果不依赖子组件的时候就地更新策略不会出现问题。但是如果你模板中列表渲染的结果依赖子组件的时候,就地更新策略就会出现问题。因为子组件内部可能会有复杂的逻辑,所以Vue监控不了子组件内部的数据。
例如下面中的例子,列表渲染输出的结果依赖子组件MyComponent(需要子组件MyComponent),然后Vue中v-for指令默认按照就地更新策略进行渲染,所以出现(图3)中出现的问题。

const MyComponent = {
  props: {
    num: Number,
  },
  template: `
  <span> {{ num }} </span>
  `
}
const App = {
  components: {
    MyComponent
  },
  data() {
    return {
      list: [
        {
          id: 1,
          value: 'item-1'
        },
        {
          id: 2,
          value: 'item-2'
        },
        {
          id: 3,
          value: 'item-3'
        }
      ],
      tempArr:[1, 2, 3]
    }
  },
  template: `
  <div>
    <ul>
      <li v-for="(item, index) of list">
        <span> {{ item.value }} - </span>
        <my-component :num="tempArr[index]"></my-component>
        <input type="text" :value="tempArr[index]" />
        <button @click="deleteItem(index)">DELETE</button>
      </li>
    </ul>
  </div>
  `,
  methods: {
    deleteItem(index) {
      this.list.splice(index, 1);
    }
  }
};

(图3)
image.png

解决就地更新遇到的问题

解决Vue中就地更新的方法就是给循环的每一个标签绑定一个唯一的key值,并且key值不能够变更。添加key值之后就没有就地更新的现象出现了,因为添加唯一key值后,Vue后面的驱动能够追踪绑定的元素。
下面的例子中我们在v-for指令下绑定唯一的key值为item.id,此时按照常理来说已经可以解决就地更新遇见的问题,但是发现还是没有效果,v-for指令渲染列表时依旧使用的是就地更新的策略。这又是为什么呢?
原因在于<my-component :num="tempArr[index]" /><input type="text" :value="tempArr[index]" />标签中,tempArr[index]属性的index属性来源于v-for指令,因为这个index值会随着绑定的数据list长度的变化而变化,所以导致绑定的元素无法对应上,所以还是会执行就地更新的策略。
这也是为什么不推荐v-for指令中,绑定key值不要绑定index的原因,因为你的增加、删除操作会导致index值的变化,而如果绑定的key值在渲染之后还会不断的变化,那么导致在Vue底层中判断新老节点的结果一致(底层源码中通过a.key ===b.key判断key值),如果结果一致的话,就执行就地更新的策略。如果绑定的key值是静态的,那么新节点绑定的key值于老节点绑定的key值肯定不同,如果底层判断新老节点的结果不一致的话,会进行打补丁patch的其它逻辑判断,不会执行就地更新的逻辑,就能够追踪每一个绑定的节点。

const MyComponent = {
  props: {
    num: Number,
  },
  template: `
  <span> {{ num }} </span>
  `
}
const App = {
  components: {
    MyComponent
  },
  data() {
    return {
      list: [
        {
          id: 1,
          value: 'item-1'
        },
        {
          id: 2,
          value: 'item-2'
        },
        {
          id: 3,
          value: 'item-3'
        }
      ],
      tempArr:[1, 2, 3]
    }
  },
  template: `
  <div>
    <ul>
      <li 
        v-for="(item, index) of list"
        :key="item.id"
        >
        <span> {{ item.value }} - </span>
        <my-component :num="tempArr[index]"></my-component>
        <input type="text" :value="tempArr[index]" />
        <button @click="deleteItem(index)">DELETE</button>
      </li>
    </ul>
  </div>
  `,
  methods: {
    deleteItem(index) {
      this.list.splice(index, 1);
    }
  }
};

通过上述的逻辑,我们修改模板template的代码,将绑定过动态index值的地方都换成静态的属性item.id。按照分析,v-for指令渲染列表时候就不会执行就地更新的策略,Vue也就能够追踪绑定的元素。从(图4)也可以看出的的确确没有执行就地更新的策略。

 template: `
  <div>
    <ul>
      <li 
        v-for="(item, index) of list"
        :key="item.id"
        >
        <span> {{ item.value }} - </span>
        <my-component :num="item.id"></my-component>
        <input type="text" :value="item.id" />
        <button @click="deleteItem(index)">DELETE</button>
      </li>
    </ul>
  </div>
  `,

(图4)
image.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1600399.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【echarts】使用 ECharts 绘制3D饼图

使用 ECharts 绘制3D饼图 在数据可视化中&#xff0c;饼图是表达数据占比信息的常见方式。ECharts 作为一个强大的数据可视化库&#xff0c;除了标准的二维饼图&#xff0c;也支持更加生动的三维饼图绘制。本文将指导你如何使用 ECharts 来创建一个3D饼图&#xff0c;提升你的…

“手撕“数组一些简单的习题

目录 1.数组转字符串 2.数组拷贝 3.求数组中元素的平均值 4.查找数组中指定元素(顺序查找) 5.查找数组中指定元素(二分查找) 6.数组排序(冒泡排序) 7.数组逆序 1.数组转字符串 先让我们看看为什么要转字符串&#xff1a; int[] arr {1,2,3,4,5,6}; System.out.printl…

性能测试 Jmeter 非 GUI 模式 -CLI 命令详解

我们在使用Jmeter做性能测试的时候&#xff0c;大部分同学用的是图形化界面进行脚本编写和执行性能测试的。但是其实真正在公司执行性能测试的时候&#xff0c;我们基本上不会用图形化界面去执行测试&#xff0c;这是因为工具渲染这些图形本身会让Jmeter结果存在很多不稳定的因…

Midjourney指南 - 生成高分辨率图片(内容已更新至V5)

Midjourney 首先为每个作业生成一个低分辨率图片网格(2x2)。你可以在选择其中任一图片&#xff0c;使用 Midjourney upscaler 来增加尺寸并添加更多细节。有多种可用于放大图像的放大模型。 每个图像网格下方的按钮用于放大所选图像。U1 U2 U3 U4 注&#xff1a;upscaler 以下…

12.MySQL应用架构演变

MySQL应用架构演变 1.总览 单机单库主从架构分库分表云数据库 2.单机单库 介绍 一个简单的小型网站或者应用背后的架构可以非常简单&#xff0c;数据存储只需要一个MySQL Instance就能满足数据读取和写入需求&#xff08;这里忽略掉了数据备份的实例&#xff09;&#xff…

【ElasticSearch】安装(bug篇)

以下解决办法参考自网友们的分享 1. JDK绑定问题 但其实这样也没有问题&#xff0c;因为内嵌的jdk版本与当前的es版本是适配的 但是&#xff0c;如果内嵌的jdk与当前es不适配&#xff0c;那就要修改配置文件 / 添加环境变量&#xff0c;让es启动的时候能扫描到我们本地的jdk …

(十二)C++自制植物大战僵尸游戏多用户存档实现(一)

植物大战僵尸游戏开发教程专栏地址http://t.csdnimg.cn/8UFMs 游戏存档 游戏存档允许玩家保存游戏进度&#xff0c;以便在之后的时间继续游戏。通过存档&#xff0c;玩家可以暂停游戏并在需要时重新开始&#xff0c;而不必从头开始或重新完成已经完成的任务。游戏通常提供多个…

RabbitMQ交换机的类型

交换机类型 可以看到&#xff0c;在订阅模型中&#xff0c;多了一个exchange角色&#xff0c;而且过程略有变化&#xff1a; Publisher&#xff1a;生产者&#xff0c;不再发送消息到队列中&#xff0c;而是发给交换机 Exchange&#xff1a;交换机&#xff0c;一方面&#xff…

基于Spring Boot的校园招聘系统

文章目录 项目介绍主要功能截图&#xff1a;部分代码展示设计总结项目获取方式 &#x1f345; 作者主页&#xff1a;超级无敌暴龙战士塔塔开 &#x1f345; 简介&#xff1a;Java领域优质创作者&#x1f3c6;、 简历模板、学习资料、面试题库【关注我&#xff0c;都给你】 &…

盲盒商城小程序(有米就出)

一款前端采用uniapp&#xff0c;后端采用Django框架开发的小程序&#xff0c;包含后台管理&#xff0c;如有人需要可联系演示功能&#xff08;个人开发&#xff0c;可商用/学习&#xff09;。 部分截图如下&#xff1a;

RabbitMQ进阶学习

在之前的练习作业中&#xff0c;我们改造了余额支付功能&#xff0c;在支付成功后利用RabbitMQ通知交易服务&#xff0c;更新业务订单状态为已支付。 但是大家思考一下&#xff0c;如果这里MQ通知失败&#xff0c;支付服务中支付流水显示支付成功&#xff0c;而交易服务中的订…

【深度学习】【机器学习】用神经网络进行入侵检测,NSL-KDD数据集,基于机器学习(深度学习)判断网络入侵,网络攻击,流量异常【3】

之前用NSL-KDD数据集做入侵检测的项目是&#xff1a; 【1】https://qq742971636.blog.csdn.net/article/details/137082925 【2】https://qq742971636.blog.csdn.net/article/details/137170933 有人问我是不是可以改代码&#xff0c;我说可以。 训练 我将NSL_KDD_Final_1.i…

企业如何安全合规地访问海外网站

国际专线、SD-WAN线路与VPN翻墙的利弊与风险 在全球化背景下&#xff0c;中国的外贸企业以及海外公司分支机构、科研研发机构等&#xff0c;都需要频繁访问海外网站以开展正常业务。然而&#xff0c;企业访问海外网站的技术方式存在一定的合规风险。本文将概述三种访问海外网站…

解读科技智慧公厕改变生活的革命性创新之路

公共厕所&#xff0c;作为城市基础设施的一部分&#xff0c;一直以来都备受人们诟病。脏乱差、设施老旧、管理混乱&#xff0c;成为公共厕所长期存在的问题。然而&#xff0c;随着科技的不断进步&#xff0c;智慧公厕应运而生&#xff0c;为解决公厕难题&#xff0c;智慧公厕源…

分析ARP解析过程

1、实验环境 主机A和主机B连接到交换机&#xff0c;并与一台路由器互连&#xff0c;如图7.17所示&#xff0c;路由器充当网关。 图7.17 实验案例一示意图 2、需求描述 查看 ARP 相关信息,熟悉在PC 和 Cisco 设备上的常用命令,设置主机A和主机B为同一个网段网关设置为路由接…

word文件的创建时间和修改时间可以更改吗?答案是肯定的 文件属性修改的方法

一&#xff0c;引言 在日常生活和工作中&#xff0c;我们经常需要处理各种Word文件。有时&#xff0c;由于某些原因&#xff0c;我们可能需要更改Word文件的创建时间和修改时间。虽然这听起来可能有些复杂&#xff0c;但实际上&#xff0c;通过一些简单的方法和工具&#xff0…

六、OpenFeign服务接口调用

一、提问 已经有loadbalancer为什么还要学习OpenFeign? 两个都有道理的话&#xff0c;日常用那个&#xff1f; 二、是什么 OpenFeign是什么 官网翻译 Feign是一个声明性web服务客户端。它使编写web服务客户端变得更容易。使用Feign创建一个接口并对其进行注释。它具有可…

HTML中div/span标签、音频标签、视频标签与特殊字符

目录 div/span标签 音频标签 视频标签 特殊字符 div/span标签 在HTML中&#xff0c;<div></div>和<span></span>是没有语义的&#xff0c;可以将两个标签当做两个盒子&#xff0c;里面可以容纳内容 两个标签有以下两个特点&#xff1a; 1. <…

48---PCIE转串口和并口电路设计

视频链接 PCIE转串口和并口电路设计01_哔哩哔哩_bilibili PCIe转串口和并口电路设计 1、PCIe转串并口电路设计基本介绍 2、PCIe转串口和并口的方案(京东) 2.1、PCIe转串口 2.1.1、ASIX (亚信)MCS9922-PCIe转2路RS232扩展卡 2.1.2、ASIX (亚信)MCS9900-PCIe转4路RS232扩展卡…

【GD32】_时钟架构及系统时钟频率配置

文章目录 一、有关时钟源二、系统时钟架构三、时钟树分析四、修改参数步骤1、设置外部晶振2、选择外部时钟源。3、 设置系统主频率大小4、修改PLL分频倍频系数 学习系统时钟架构和时钟树&#xff0c;验证及学习笔记如下&#xff0c;如有错误&#xff0c;欢迎指正。主要记录了总…