热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

v3+ts慢慢学习之路四【vfor和它当中的key】

“会倾听”比“会表达”更为难得一、v-for支持的类型1、遍历数组v-for遍历数组一:“itemin数组”v-for遍历数组二:“(item,in

“会倾听” 比 “会表达”更为难得


一、v-for支持的类型


1、遍历数组

  • v-for遍历数组一: “item in 数组”
  • v-for遍历数组二 :“(item,index) in 数组”

2、遍历对象

  • v-for遍历对象一: “value in 对象”
  • v-for遍历对象二:“(value,key) in 对象”
  • v-for遍历对象三:“(value,key,index) in 对象”

3、遍历数字

  • v-for遍历对数字: “item in 数字”
  • 每一个item都是一个数字
    在这里插入图片描述

二、 v-for中的key是什么作用


先看一段官方对v-for的介绍

在这里插入图片描述
在这里插入图片描述

简单翻译总结:
1、 key主要用于Vue的虚拟DOM算法,在新旧nodes对比时辨别VNodes;
2、不使用key,Vue会使用一种算法尽可能的尝试就地修改/复用相同类型元素;
3、使用key时,它会基于key的变化重新排列元素顺序,并且会移除/销毁key不存在的元素;

遇事不决,就先看看源码,Vue到是怎么区分

源码中是如何区分有key和没有key的

在这里插入图片描述

未使用KEY的情况下

// 未使用key的情况下
const patchUnkeyedChildren &#61; (c1: VNode[],c2: VNodeArrayChildren,container: RendererElement,anchor: RendererNode | null,parentComponent: ComponentInternalInstance | null,parentSuspense: SuspenseBoundary | null,isSVG: boolean,optimized: boolean) &#61;> {c1 &#61; c1 || EMPTY_ARRc2 &#61; c2 || EMPTY_ARR// 1、旧节点长度const oldLength &#61; c1.length// 2、新节点长度const newLength &#61; c2.length// 3、获取新旧节点最小的长度const commonLength &#61; Math.min(oldLength, newLength)let i// 4、从0的位置开始依次patch比较for (i &#61; 0; i < commonLength; i&#43;&#43;) {const nextChild &#61; (c2[i] &#61; optimized? cloneIfMounted(c2[i] as VNode): normalizeVNode(c2[i]))patch(c1[i],nextChild,container,null,parentComponent,parentSuspense,isSVG,optimized)}// 5、如果旧节点长度依旧大于新节点长度&#xff0c;那么就移除旧节点if (oldLength > newLength) {// remove oldunmountChildren(c1, parentComponent, parentSuspense, true, commonLength)} else {// 6、否则就创建新节点// mount newmountChildren(c2,container,anchor,parentComponent,parentSuspense,isSVG,optimized,commonLength)}}

未使用KEY的过程如下图

在这里插入图片描述

使用KEY的情况下

// 在使用KEY的情况下
// can be all-keyed or mixedconst patchKeyedChildren &#61; (c1: VNode[],c2: VNodeArrayChildren,container: RendererElement,parentAnchor: RendererNode | null,parentComponent: ComponentInternalInstance | null,parentSuspense: SuspenseBoundary | null,isSVG: boolean,optimized: boolean) &#61;> {let i &#61; 0const l2 &#61; c2.lengthlet e1 &#61; c1.length - 1 // prev ending indexlet e2 &#61; l2 - 1 // next ending index// 1、从头部开始遍历&#xff0c;遇到相同节点就继续&#xff0c;遇到不同的就跳出循环// 1. sync from start// (a b) c// (a b) d ewhile (i <&#61; e1 && i <&#61; e2) {const n1 &#61; c1[i]const n2 &#61; (c2[i] &#61; optimized? cloneIfMounted(c2[i] as VNode): normalizeVNode(c2[i]))if (isSameVNodeType(n1, n2)) {patch(n1,n2,container,null,parentComponent,parentSuspense,isSVG,optimized)} else {break}i&#43;&#43;}// 2、从尾部开始遍历&#xff0c;遇到相同节点就继续&#xff0c;遇到不同就跳出循环// 2. sync from end// a (b c)// d e (b c)while (i <&#61; e1 && i <&#61; e2) {const n1 &#61; c1[e1]const n2 &#61; (c2[e2] &#61; optimized? cloneIfMounted(c2[e2] as VNode): normalizeVNode(c2[e2]))if (isSameVNodeType(n1, n2)) {patch(n1,n2,container,null,parentComponent,parentSuspense,isSVG,optimized)} else {break}e1--e2--}// 3、如果旧节点遍历完了&#xff0c;依然有新的节点&#xff0c;那么新的节点就是添加// 3. common sequence &#43; mount// (a b)// (a b) c// i &#61; 2, e1 &#61; 1, e2 &#61; 2// (a b)// c (a b)// i &#61; 0, e1 &#61; -1, e2 &#61; 0if (i > e1) {if (i <&#61; e2) {const nextPos &#61; e2 &#43; 1const anchor &#61; nextPos < l2 ? (c2[nextPos] as VNode).el : parentAnchorwhile (i <&#61; e2) {patch(null,(c2[i] &#61; optimized? cloneIfMounted(c2[i] as VNode): normalizeVNode(c2[i])),container,anchor,parentComponent,parentSuspense,isSVG)i&#43;&#43;}}}// 4、如果新节点遍历完了&#xff0c;还有旧的节点&#xff0c;那么旧节点就是移除的// 4. common sequence &#43; unmount// (a b) c// (a b)// i &#61; 2, e1 &#61; 2, e2 &#61; 1// a (b c)// (b c)// i &#61; 0, e1 &#61; 0, e2 &#61; -1else if (i > e2) {while (i <&#61; e1) {unmount(c1[i], parentComponent, parentSuspense, true)i&#43;&#43;}}// 5、如果中间存在不知道如何排列的位置序列&#xff0c;那么就是用key建立索引图最大限度的使用旧节点// 5. unknown sequence// [i ... e1 &#43; 1]: a b [c d e] f g// [i ... e2 &#43; 1]: a b [e d c h] f g// i &#61; 2, e1 &#61; 4, e2 &#61; 5else {const s1 &#61; i // prev starting indexconst s2 &#61; i // next starting index// 5.1 build key:index map for newChildrenconst keyToNewIndexMap: Map<string | number, number> &#61; new Map()for (i &#61; s2; i <&#61; e2; i&#43;&#43;) {const nextChild &#61; (c2[i] &#61; optimized? cloneIfMounted(c2[i] as VNode): normalizeVNode(c2[i]))if (nextChild.key !&#61; null) {if (__DEV__ && keyToNewIndexMap.has(nextChild.key)) {warn(&#96;Duplicate keys found during update:&#96;,JSON.stringify(nextChild.key),&#96;Make sure keys are unique.&#96;)}keyToNewIndexMap.set(nextChild.key, i)}}// 5.2 loop through old children left to be patched and try to patch// matching nodes & remove nodes that are no longer presentlet jlet patched &#61; 0const toBePatched &#61; e2 - s2 &#43; 1let moved &#61; false// used to track whether any node has movedlet maxNewIndexSoFar &#61; 0// works as Map// Note that oldIndex is offset by &#43;1// and oldIndex &#61; 0 is a special value indicating the new node has// no corresponding old node.// used for determining longest stable subsequenceconst newIndexToOldIndexMap &#61; new Array(toBePatched)for (i &#61; 0; i < toBePatched; i&#43;&#43;) newIndexToOldIndexMap[i] &#61; 0for (i &#61; s1; i <&#61; e1; i&#43;&#43;) {const prevChild &#61; c1[i]if (patched >&#61; toBePatched) {// all new children have been patched so this can only be a removalunmount(prevChild, parentComponent, parentSuspense, true)continue}let newIndexif (prevChild.key !&#61; null) {newIndex &#61; keyToNewIndexMap.get(prevChild.key)} else {// key-less node, try to locate a key-less node of the same typefor (j &#61; s2; j <&#61; e2; j&#43;&#43;) {if (newIndexToOldIndexMap[j - s2] &#61;&#61;&#61; 0 &&isSameVNodeType(prevChild, c2[j] as VNode)) {newIndex &#61; jbreak}}}if (newIndex &#61;&#61;&#61; undefined) {unmount(prevChild, parentComponent, parentSuspense, true)} else {newIndexToOldIndexMap[newIndex - s2] &#61; i &#43; 1if (newIndex >&#61; maxNewIndexSoFar) {maxNewIndexSoFar &#61; newIndex} else {moved &#61; true}patch(prevChild,c2[newIndex] as VNode,container,null,parentComponent,parentSuspense,isSVG,optimized)patched&#43;&#43;}}// 5.3 move and mount// generate longest stable subsequence only when nodes have movedconst increasingNewIndexSequence &#61; moved? getSequence(newIndexToOldIndexMap): EMPTY_ARRj &#61; increasingNewIndexSequence.length - 1// looping backwards so that we can use last patched node as anchorfor (i &#61; toBePatched - 1; i >&#61; 0; i--) {const nextIndex &#61; s2 &#43; iconst nextChild &#61; c2[nextIndex] as VNodeconst anchor &#61;nextIndex &#43; 1 < l2 ? (c2[nextIndex &#43; 1] as VNode).el : parentAnchorif (newIndexToOldIndexMap[i] &#61;&#61;&#61; 0) {// mount newpatch(null,nextChild,container,anchor,parentComponent,parentSuspense,isSVG)} else if (moved) {// move if:// There is no stable subsequence (e.g. a reverse)// OR current node is not among the stable sequenceif (j < 0 || i !&#61;&#61; increasingNewIndexSequence[j]) {move(nextChild, container, anchor, MoveType.REORDER)} else {j--}}}}}

使用KEY的过程如下图

在这里插入图片描述

总结&#xff1a;Vue在进行diff算法的时候&#xff0c;会尽量利用我们的key来进行优化操作
1、在没有key的时候我们的效率是非常低效的
2、在进行插入或者重置顺序的时候&#xff0c;保持相同的key可以让diff算法更加的高效


推荐阅读
  • This article discusses the efficiency of using char str[] and char *str and whether there is any reason to prefer one over the other. It explains the difference between the two and provides an example to illustrate their usage. ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • 本文介绍了P1651题目的描述和要求,以及计算能搭建的塔的最大高度的方法。通过动态规划和状压技术,将问题转化为求解差值的问题,并定义了相应的状态。最终得出了计算最大高度的解法。 ... [详细]
  • Whatsthedifferencebetweento_aandto_ary?to_a和to_ary有什么区别? ... [详细]
  • 本文介绍了使用哈夫曼树实现文件压缩和解压的方法。首先对数据结构课程设计中的代码进行了分析,包括使用时间调用、常量定义和统计文件中各个字符时相关的结构体。然后讨论了哈夫曼树的实现原理和算法。最后介绍了文件压缩和解压的具体步骤,包括字符统计、构建哈夫曼树、生成编码表、编码和解码过程。通过实例演示了文件压缩和解压的效果。本文的内容对于理解哈夫曼树的实现原理和应用具有一定的参考价值。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • 本文介绍了最长上升子序列问题的一个变种解法,通过记录拐点的位置,将问题拆分为左右两个LIS问题。详细讲解了算法的实现过程,并给出了相应的代码。 ... [详细]
  • IjustinheritedsomewebpageswhichusesMooTools.IneverusedMooTools.NowIneedtoaddsomef ... [详细]
  • 本文记录了在vue cli 3.x中移除console的一些采坑经验,通过使用uglifyjs-webpack-plugin插件,在vue.config.js中进行相关配置,包括设置minimizer、UglifyJsPlugin和compress等参数,最终成功移除了console。同时,还包括了一些可能出现的报错情况和解决方法。 ... [详细]
  • VueCLI多页分目录打包的步骤记录
    本文介绍了使用VueCLI进行多页分目录打包的步骤,包括页面目录结构、安装依赖、获取Vue CLI需要的多页对象等内容。同时还提供了自定义不同模块页面标题的方法。 ... [详细]
  • 先看看ElementUI里关于el-table的template数据结构:<template><el-table:datatableData><e ... [详细]
author-avatar
青岛淘宝摄影
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有