【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.展示说明...

C++初阶-list的底层
目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...

微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】
微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来,Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...

【网络安全产品大调研系列】2. 体验漏洞扫描
前言 2023 年漏洞扫描服务市场规模预计为 3.06(十亿美元)。漏洞扫描服务市场行业预计将从 2024 年的 3.48(十亿美元)增长到 2032 年的 9.54(十亿美元)。预测期内漏洞扫描服务市场 CAGR(增长率&…...

渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止
<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet: https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...
Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务
通过akshare库,获取股票数据,并生成TabPFN这个模型 可以识别、处理的格式,写一个完整的预处理示例,并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务,进行预测并输…...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...

Mac下Android Studio扫描根目录卡死问题记录
环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中,提示一个依赖外部头文件的cpp源文件需要同步,点…...

接口自动化测试:HttpRunner基础
相关文档 HttpRunner V3.x中文文档 HttpRunner 用户指南 使用HttpRunner 3.x实现接口自动化测试 HttpRunner介绍 HttpRunner 是一个开源的 API 测试工具,支持 HTTP(S)/HTTP2/WebSocket/RPC 等网络协议,涵盖接口测试、性能测试、数字体验监测等测试类型…...

Unity UGUI Button事件流程
场景结构 测试代码 public class TestBtn : MonoBehaviour {void Start(){var btn GetComponent<Button>();btn.onClick.AddListener(OnClick);}private void OnClick(){Debug.Log("666");}}当添加事件时 // 实例化一个ButtonClickedEvent的事件 [Formerl…...
Modbus RTU与Modbus TCP详解指南
目录 1. Modbus协议基础 1.1 什么是Modbus? 1.2 Modbus协议历史 1.3 Modbus协议族 1.4 Modbus通信模型 🎭 主从架构 🔄 请求响应模式 2. Modbus RTU详解 2.1 RTU是什么? 2.2 RTU物理层 🔌 连接方式 ⚡ 通信参数 2.3 RTU数据帧格式 📦 帧结构详解 🔍…...