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

news/2024/10/22 12:24:05/

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>标签内部。

javascript">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元素的重绘与回流,减少浏览器的性能消耗。

javascript">const App = {data() {return {isLogin: false}},template:`<divv-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)中的问题。

javascript">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)中同样的问题。

javascript">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)中出现的问题。

javascript">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的其它逻辑判断,不会执行就地更新的逻辑,就能够追踪每一个绑定的节点。

javascript">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)也可以看出的的确确没有执行就地更新的策略。

javascript"> 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.ppmy.cn/news/1425774.html

相关文章

Cesium简单案例

一、Cesium组件 1、HTML <template><div id"cesiumContainer"><!-- 地图工具栏 --><ul class"mapTools"><li v-for"item in toolsData" :key"item.id" click"toolsClick(item)"><!-- 显…

【Maven】使用Maven创建模块的详细过程

在Maven中&#xff0c;通常会有一个父项目&#xff08;parent project&#xff09;和多个子模块&#xff08;submodules&#xff09;。父项目用于定义整个项目范围内的配置&#xff0c;如依赖管理、插件配置等&#xff0c;而子模块则是具体的执行单元。以下是详细的步骤和一些实…

Apache Tomcat 简单使用

Apache Tomcat 下载 download Tomcat服务器是一个免费的开放源代码的Web应用服务器&#xff0c;属于轻量级应用服务器&#xff0c;在中小型系统和并发访问用户不是很多的场合下被普遍使用&#xff0c;是开发和调试JSP程序的首选。 支持JDK版本 JDK8 要下载tomcat 9.0.x版本 …

Vue的图片懒加载 vue-lazyload插件使用

图片懒加载是一种网页性能优化技术&#xff0c;页面加载时仅加载可见区域内的图像&#xff0c;图像只会在用户滚动或浏览到它们时才会被加载&#xff0c;而不是在页面一开始就全部加载。 优点 可以减少首页首次加载的数量&#xff0c;减少服务器的压力有占位图片来展示预加载动…

rman 归档备份 archived log 不重复备份

11.2.0.3数据库环境&#xff0c;使用rman进行归档日志备份&#xff0c;想实现&#xff1a; (1)每天备份归档日志&#xff0c;备份完并不删除归档日志 (2)归档日志备份成功一次之后&#xff0c;下次再备份的时候rman就自动不会再次备份这个归档日志 这个需求可以通过 backup …

K8s: 在Pod中将configmap数据注入容器

configMap 概述 文档: https://kubernetes.io/zh-cn/docs/concepts/configuration/configmap/ Kubernetes 为我们提供了 ConfigMap&#xff0c;可以方便的配置一些变量 是一个存储键值对 key-value 对象的 创建一个可以包含多个键值对的 ConfigMap, 以下是&#xff1a;mul-c…

DNS服务器配置与管理(2)——BIND部署DNS

在Linux上配置DNS的常用软件是BIND&#xff08;Berkeley Internet Name Domain Service&#xff0c;BIND&#xff09;&#xff0c;它是一款实现DNS服务器的开放源码软件。本文详细介绍了在CentOS7上安装并配置Bind软件。 一、Bind软件介绍 BIND包最初是在 1980 年代初在加州大…

OpenCV基本图像处理操作(四)——傅立叶变换

傅里叶变换的作用 高频&#xff1a;变化剧烈的灰度分量&#xff0c;例如边界 低频&#xff1a;变化缓慢的灰度分量&#xff0c;例如一片大海 滤波 低通滤波器&#xff1a;只保留低频&#xff0c;会使得图像模糊 高通滤波器&#xff1a;只保留高频&#xff0c;会使得图像细节…