【Vue面试题十七】、你知道vue中key的原理吗?说说你对它的理解
文章底部有个人公众号:热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享? 踩过的坑没必要让别人在再踩,自己复盘也能加深记忆。利己利人、所谓双赢。
面试官:你知道vue中key的原理吗?说说你对它的理解
一、Key是什么
开始之前,我们先还原两个实际工作场景
1、当我们在使用v-fo
r时,需要给单元加上key
<ul><li v-for="item in items" :key="item.id">...</li>
</ul>
2、用+new Date()生成的时间戳作为key,手动强制触发重新渲染
<Comp :key="+new Date()" />
那么这背后的逻辑是什么,key
的作用又是什么?
一句话来讲
key是给每一个vnode的唯一id,也是diff的一种优化策略,可以根据key,更准确, 更快的找到对应的vnode节点
场景背后的逻辑
当我们在使用v-for
时,需要给单元加上key
-
如果不用
key
,Vue
会采用就地复地原则:最小化element
的移动,并且会尝试尽最大程度在同适当的地方对相同类型的element
,做patch
或者reuse
。 -
如果使用了
key
,Vue
会根据keys
的顺序记录element
,曾经拥有了key
的element
如果不再出现的话,会被直接remove
或者destoryed
用+new Date()
生成的时间戳作为key
,手动强制触发重新渲染
- 当拥有新值的
rerender
作为key
时,拥有了新key
的Comp
出现了,那么旧key Comp
会被移除,新key Comp
触发渲染
二、设置key与不设置key区别
举个例子:
创建一个实例,2秒后往items数组插入数据
<body><div id="demo"><p v-for="item in items" :key="item">{{item}}</p></div><script src="../../dist/vue.js"></script><script>// 创建实例const app = new Vue({el: '#demo',data: { items: ['a', 'b', 'c', 'd', 'e'] },mounted () {setTimeout(() => { this.items.splice(2, 0, 'f') // }, 2000);},});</script>
</body>
在不使用key的情况,vue会进行这样的操作:
分析下整体流程:
- 比较A,A,相同类型的节点,进行
patch
,但数据相同,不发生dom
操作 - 比较B,B,相同类型的节点,进行
patch
,但数据相同,不发生dom
操作 - 比较C,F,相同类型的节点,进行
patch
,数据不同,发生dom
操作 - 比较D,C,相同类型的节点,进行
patch
,数据不同,发生dom
操作 - 比较E,D,相同类型的节点,进行
patch
,数据不同,发生dom
操作 - 循环结束,将E插入到
DOM
中
一共发生了3次更新,1次插入操作
在使用key
的情况:vue
会进行这样的操作:
比较A,A,相同类型的节点,进行patch
,但数据相同,不发生dom
操作
比较B,B,相同类型的节点,进行patch
,但数据相同,不发生dom
操作
比较C,F,不相同类型的节点
比较E、E,相同类型的节点,进行patch
,但数据相同,不发生dom
操作
比较D、D,相同类型的节点,进行patch
,但数据相同,不发生dom
操作
比较C、C,相同类型的节点,进行patch
,但数据相同,不发生dom
操作
循环结束,将F插入到C之前
一共发生了0次更新,1次插入操作
通过上面两个小例子,可见设置key
能够大大减少对页面的DOM
操作,提高了diff
效率
设置key值一定能提高diff效率吗?
其实不然,文档中也明确表示
当 Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用“就地复用”策略。如果数据项的顺序被改变,Vue 将不会移动 DOM
元素来匹配数据项的顺序, 而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素
这个默认的模式是高效的,但是只适用于不依赖子组件状态或临时 DOM
状态 (例如:表单输入值) 的列表渲染输出
建议尽可能在使用 v-for
时提供 key
,除非遍历输出的 DOM
内容非常简单,或者是刻意依赖默认行为以获取性能上的提升
三、原理分析
源码位置:core/vdom/patch.js
这里判断是否为同一个key
,首先判断的是key
值是否相等如果没有设置key
,那么key
为undefined
,这时候undefined
是恒等于undefined
function sameVnode (a, b) {return (a.key === b.key && ((a.tag === b.tag &&a.isComment === b.isComment &&isDef(a.data) === isDef(b.data) &&sameInputType(a, b)) || (isTrue(a.isAsyncPlaceholder) &&a.asyncFactory === b.asyncFactory &&isUndef(b.asyncFactory.error))))
}
updateChildren
方法中会对新旧vnode
进行diff
,然后将比对出的结果用来更新真实的DOM
function updateChildren (parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) {...while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {if (isUndef(oldStartVnode)) {...} else if (isUndef(oldEndVnode)) {...} else if (sameVnode(oldStartVnode, newStartVnode)) {...} else if (sameVnode(oldEndVnode, newEndVnode)) {...} else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right...} else if (sameVnode(oldEndVnode, newStartVnode)) { // Vnode moved left...} else {if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)idxInOld = isDef(newStartVnode.key)? oldKeyToIdx[newStartVnode.key]: findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)if (isUndef(idxInOld)) { // New elementcreateElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx)} else {vnodeToMove = oldCh[idxInOld]if (sameVnode(vnodeToMove, newStartVnode)) {patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)oldCh[idxInOld] = undefinedcanMove && nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm)} else {// same key but different element. treat as new elementcreateElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx)}}newStartVnode = newCh[++newStartIdx]}}...
}
相关文章:

【Vue面试题十七】、你知道vue中key的原理吗?说说你对它的理解
文章底部有个人公众号:热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享? 踩过的坑没必要让别人在再踩,自己复盘也能加深记忆。利己利人、所谓双赢。 面试官:你知道vue中key的原理吗…...

【数据结构】二叉树--堆排序
目录 一 降序(建小堆) 二 升序 (建大堆) 三 优化(以升序为例) 四 TOP-K问题 一 降序(建小堆) void Swap(int* x, int* y) {int tmp *x;*x *y;*y tmp; }//降序 建小堆 void AdjustUp(int* a, int child) {int parent (child - 1) / 2;while (child > 0){if (a[chil…...

项目log日志mysql记录,熟悉python的orm框架
直接在项目里面创建一个class,这个类对应着mysql里面的表 我们运行项目,可以自动建立表 在.env中找到mysql的配置信息,这个是在NB服务器上运行的mysql,localhost需要变成NB服务器的ipv4地址 使用Mysql工具连接查看,连…...

【数据结构-字符串 四】【字符串识别】字符串转为整数、比较版本号
废话不多说,喊一句号子鼓励自己:程序员永不失业,程序员走向架构!本篇Blog的主题是【字符串转换】,使用【字符串】这个基本的数据结构来实现,这个高频题的站点是:CodeTop,筛选条件为&…...

React 组件传 children 的各种方案
自定义组件的时候往往需要传 children,由于写法比较多样,我就总结了一下。 方案列表 1. 类组件1.1 类组件,不使用解构1.2 类组件,使用解构 2. 函数组件2.1 函数组件,不使用解构2.2 函数组件,外部解构2.3 函…...

如何在一个传统的html中,引入vueJs并使用vue复制组件?
如何在一个传统的html中,引入vueJs并使用vue复制组件? 1.1 引言1.2 背景1.3 解决方案1.3.1 解决方案一:直接使用clipboard(不推荐仅供参考学习)1.3.2 解决方案二:封装指令js库后使用 (推荐) 1.1 引言 这篇博文主要分享如何在一个…...

【轻松玩转MacOS】故障排除篇
引言 在使用 MacOS 时,遇到故障是在所难免的。不要担心,这篇文章将为您提供一些常见的故障排除步骤,并介绍如何联系苹果的支持团队寻求帮助。让我们一起来看看吧! 一、常见的故障排除步骤 1.1 网络连接问题 如果你发现你的Mac…...

Linux基本指令(1)
Linux基本指令(1) 1.ls指令1.1ls的用法 2. pwd指令3.cd指令3.1 cd3.2补充内容3.3 cd - 指令3.4 cd ~ 指令 4. touch指令4.1stat指令 5.mkdir 指令6.rmdir/rm指令6.1补充内容 7.man指令8.nano 指令9.cat指令10 cp指令11 mv指令12 echo指令12.1 > 输出重…...

计算机毕业设计选题推荐-springboot 网上手机销售系统
✍✍计算机编程指导师 ⭐⭐个人介绍:自己非常喜欢研究技术问题!专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目:有源码或者技术上的问题欢迎在评论区一起讨论交流! ⚡⚡ Java实战 |…...

2、vscode c++ 项目配置调试及运行
文章目录 1、项目布局2、多项目管理2.1 先是一个总的CMakeLists.txt2.2 每个项目2.3 多版本OPENCV 3、调试和运行 接上一篇文章,vscode和cmake的c环境配置好以后,我们要写项目,再写对应的CMakeLists.txt 1、项目布局 . ├── bin ├── bu…...
二叉搜索树的最近公共祖先
二叉搜索树的最近公共祖先-力扣 235 题 求二叉搜索树最近公共祖先(祖先也包括自己) 前提: 1.节点值唯一 2.p和q都存在 要点:若 p,q 在 ancestor 的两侧,则 ancestor 就是它们的最近公共祖先 解题思路&…...
LuatOS-SOC接口文档(air780E)-- i2c - I2C操作
常量 常量 类型 解释 i2c.FAST number 高速 i2c.SLOW number 低速 i2c.exist(id) i2c编号是否存在 参数 传入值类型 解释 int 设备id, 例如i2c1的id为1, i2c2的id为2 返回值 返回值类型 解释 bool 存在就返回true,否则返回false 例子 -- 检查i2c1是否存…...

帝国cms改目录后打不开,帝国cms改目录生成后还是404
帝国CMS更改了网站域名或者栏目目录地址信息打不开的解决方法,一起来看看吧: 很多的小伙伴们,改了后台的系统设置里面的网站地址或者栏目目录地址,信息页就打不开的解决方法如下: 后台>系统>数据更新>更新信…...

计算机毕业设计选什么题目好?springboot智慧养老中心管理系统
✍✍计算机编程指导师 ⭐⭐个人介绍:自己非常喜欢研究技术问题!专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目:有源码或者技术上的问题欢迎在评论区一起讨论交流! ⚡⚡ Java实战 |…...

创建一个基本的win32窗口
1.建立一个窗口的基本步骤 (1)向系统注册一个窗体类 (2)根据窗体类创建窗口 (3)进入消息循环 2.程序结构 (1)主函数的输入参数 int WINAPI WinMain( HISTANCE hInstance,//当前窗口的句柄 HINSTANCE hPr…...

如何在 Spring Boot 中使用 WebSocket
在Spring Boot中使用WebSocket构建实时应用 WebSocket是一种用于实现双向通信的网络协议,它非常适合构建实时应用程序,如在线聊天、实时通知和多人协作工具。Spring Boot提供了对WebSocket的支持,使得在应用程序中集成WebSocket变得非常容易…...
ubuntu2023装完显卡驱动和CUDA CUDNN开机只有下划线闪烁
解决方法 网上很多方案,如Ubuntu开机后卡死只有左上角有一个下划线不停闪烁_ubuntu开机左上角横杠一直闪-CSDN博客,原因是显卡驱动和系统内核不兼容,解决方案是CtrlAltF2打开tty模式进行问题检查 但是我CtrlAltF2完全没反应。 于是…...
MySQL三种安装方法(yum安装、编译安装、二进制安装)
mysql安装 一、yum安装方式二、编译安装方式三、二进制安装方式 切记:一定要关闭防火墙和selinux!!! 服务器配置:2C4G即可,一台 一、yum安装方式 mysql的官方网站:www.mysql.com 中文官网&…...

《视觉 SLAM 十四讲》第 7 讲 视觉里程计1 【如何根据图像 估计 相机运动】【特征点法】
github源码链接V2 文章目录 第 7 讲 视觉里程计17.1 特征点法7.1.1 特征点7.1.2 ORB 特征FAST 关键点 ⟹ \Longrightarrow ⟹ Oriented FASTBRIEF 描述子 7.1.3 特征匹配 7.2 实践 【Code】本讲 CMakeLists.txt 7.2.1 使用 OpenCV 进行 ORB 的特征匹配 【Code】7.2.2 手写 O…...

9. 一个SpringBoot项目运行
新手如何运行一个SpringBoot项目 1.SpringBoot项目运行 新创建的SpringBoot项目如何运行 2.启动lombok注解 点击该按钮,启动lombok注解支持 3.展示说明...

通过Wrangler CLI在worker中创建数据库和表
官方使用文档:Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后,会在本地和远程创建数据库: npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库: 现在,您的Cloudfla…...
多场景 OkHttpClient 管理器 - Android 网络通信解决方案
下面是一个完整的 Android 实现,展示如何创建和管理多个 OkHttpClient 实例,分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...
在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module
1、为什么要修改 CONNECT 报文? 多租户隔离:自动为接入设备追加租户前缀,后端按 ClientID 拆分队列。零代码鉴权:将入站用户名替换为 OAuth Access-Token,后端 Broker 统一校验。灰度发布:根据 IP/地理位写…...
C++.OpenGL (10/64)基础光照(Basic Lighting)
基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...

C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。
1.先用Visual Studio 2017打开Yichip YC31xx loader.vcxproj,再用Visual Studio 2022打开。再保侟就有.sln文件了。 易兆微芯片下载工具加开机动画下载 ExtraDownloadFile1Info.\logo.bin|0|0|10D2000|0 MFC应用兼容CMD 在BOOL CYichipYC31xxloaderDlg::OnIni…...
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南 在数字化营销时代,邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天,我们将深入解析邮件打开率、网站可用性、页面参与时…...

NXP S32K146 T-Box 携手 SD NAND(贴片式TF卡):驱动汽车智能革新的黄金组合
在汽车智能化的汹涌浪潮中,车辆不再仅仅是传统的交通工具,而是逐步演变为高度智能的移动终端。这一转变的核心支撑,来自于车内关键技术的深度融合与协同创新。车载远程信息处理盒(T-Box)方案:NXP S32K146 与…...

视觉slam十四讲实践部分记录——ch2、ch3
ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...
jmeter聚合报告中参数详解
sample、average、min、max、90%line、95%line,99%line、Error错误率、吞吐量Thoughput、KB/sec每秒传输的数据量 sample(样本数) 表示测试中发送的请求数量,即测试执行了多少次请求。 单位,以个或者次数表示。 示例:…...
从面试角度回答Android中ContentProvider启动原理
Android中ContentProvider原理的面试角度解析,分为已启动和未启动两种场景: 一、ContentProvider已启动的情况 1. 核心流程 触发条件:当其他组件(如Activity、Service)通过ContentR…...