当前位置: 首页 > Web前端 > vue.js

在Vue的v-for中,key为什么不能使用index?_0

时间:2023-03-31 16:22:47 vue.js

写在前端,主要是DOM相关操作和JS相关,我们都知道DOM操作是比较耗时的,所以我们在写前端相关代码的时候,如何减少不必要的DOM操作就成了前端优化的重要组成部分。VirtualDOM(虚拟DOM)在jQuery时代,基本上所有DOM相关的操作都是自己写的(当然博主没写过jQuery,可能博主太年轻,错过了jQuery大法时代),如何操作DOM,如何安排操作DOM的时机是决定性能的关键,而在Vue、React等框架盛行的时代,框架采用数据驱动的视图,封装了大量的DOM操作细节,让更多的DOM操作细节的优化已经从开发者自己的选择和控制转移到框架上。在学会使用框架之后,如果想更深入地学习框架,就需要了解框架封装的底层原理。其中一个很核心的部分就是虚拟DOM(virtualDOM)什么是虚拟DOM?简而言之,就是通过JS模拟DOM结构。纠结用什么JS数据结构来模拟DOM,并没有一套标准。只要能完整覆盖DOM的所有结构,下面就用更通用的方式来演示一下。通过对DOM结构的分析,我们可以用tag表示DOM节点的类型,props表示DOM节点的所有属性,包括style,class等,children表示子节点(没有子节点表示内容),所以我们把整个DOM都用JS模拟出来,然后呢?那就看下一章吧~~~//DOM嘿嘿~~我是义乌

//VDOMletvdom={tag:'div',props:{classname:'container',},children:[{tag:'h1',props:{classname:'title',style:{color:'black'}},孩子们:'HeiHei~~'},{tag:'div',props:{classname:'inner-box',},children:[{tag:'span',props:{classname:'myname'},children:'我是Yimwu'}]}]}虚拟DOM的作用当我们可以在JS中模拟DOM结构时,我们就可以通过JS优化DOM操作。如何优化呢?这时,差是时候让法律现身了当我们通过JS修改DOM时,不会直接触发DOM更新,而是会先生成一个新的虚拟DOM,然后使用diff算法与修改前生成的虚拟DOM进行比较,找出需要修改的点被修改。最后才是真正的DOM更新操作。Vue源码中diff算法的patch.js路径。Vue中diff算法的相关代码主要在patch.js文件中。路径如下图所示。patch函数1.如果新节点不存在(vnode未定义),直接执行destroyhook并返回2.如果旧节点不存在(oldVnode未定义),直接创建新节点3.如果新节点和旧节点存在,进入下一级判断,比较节点参考:前端vue面试题详解4、使用sameVnode函数判断新节点和旧节点是否为同一个节点。如果相同,则递进比较它们的子节点。如果不同,直接重新创建新节点。patchVnode函数1.如果新节点是文本节点(isUndef(vnode.text)===false)且新旧节点文本不同(oldVnode.text!==vnode.text),则直接设置(setTextContent)元素的文本(ele)2.如果新节点不是文本节点,则划分对于以下情况2.1,如果新节点和旧节点都有子节点,则调用updateChildren更新子节点2.2,如果只有新节点有子节点,则直接添加子节点(addVnode)2.3,如果只有旧节点有子节点,则直接删除子节点(removeVnodes)2.4。如果旧节点有文本,删除文本(setTextContext)updateChildrenupdateChildren函数使用双端diff,所谓双端,即同时比较新旧节点的两端和中间,比较步骤如下:1、新起始节点vs旧起始节点,如果相同,直接遍历其子节点,调用patchVnode比较子元素差异,指针向前移动2、新结束节点vs旧的结束节点,如果相同则直接遍历其子节点,调用patchVnode比较子元素差异,指针向前移动一步3.旧的起始节点vs新的结束节点,如果相同则先将新的结束节点移动到旧起始节点之前的位置,然后遍历它的children,调用patchVnode比较子元素差异,将指针向前移动一步4.旧的结束节点vs新的起始节点,如果相同,先将新的起始节点移动到旧的结束节点后面的位置,然后遍历其子节点,调用patchVnode比较子元素差异,指针前移5步。如果前面4种情况都没有命中,则遍历新节点,将子节点组与子节点组进行比较老节点的子节点一个一个,比较会一个一个遍历。没有匹配则直接在元素diff算法中重建Key值。我们从diff算法的updateChildren函数得知,使用双端diff算法会比较新的起止节点和旧的起止节点。当没有匹配到时会采用全遍历的方式进行一对一的比较,那么这个时候key就发挥作用了。当我们从新节点开始遍历该节点,与旧节点进行匹配时,如果key匹配,那么说明该元素只是位置发生了移动,只需调整位置并查看其子节点(sameVnode)即可,无需完全重建元素,大大节省了性能v-for中的key值可以是index吗?答案当然是否定的。例如,让我们看一下以下两个vdom。从num值可以看出,新旧vdoms是由两个数组以相反的顺序生成的。正常的vdom安装方式应该是简单的改一下顺序,直接复用3个元素。当我们使用索引作为键时,情况就不同了。由于索引总是从0开始,所以这两个vdom的key值从头到尾看起来都是一样的,这就导致我们比较key值的时候,会发现每一个都匹配,然后patchVnode它的child节点。这时,由于props不同,即num不同,所以会触发对应响应值的更新机制,过程中会调用多个更新相关的钩子函数。如果定义的属性很多,触发更新会造成非常大的性能损失,因此,在使用v-for时,建议使用id等唯一标识的字段,不要使用index,避免不必要的性能损失!constoldVdom={tag:"div",children:[{tag:"div",key:0,num:1},{tag:"div",key:1,num:2},{tag:"div"}",key:2,num:3},]}constnewVdom={tag:"div",children:[{tag:"div",key:2,num:3},{tag:"div",key:0,num:1},{tag:"div",key:1,num:2},]}总结学习VDOM和diff算法,实现前端对性能的极致追求,通读vdom源代码,可以从更深的角度基本理解使用VDOM的目的,以及key值在diff算法中的真实作用,也可以从更底层的角度理解为什么不推荐索引作为key的BestPractices!