• vue3 不推荐使用index作为v-for遍历的key


    结论: 

    1. 使用index 作为key时, 当逆序添加或者逆序删除的时候会破坏数据结构,从而产生没必要的dom更新,导致效率低。
    2. 用index作为key时,如果结构中包含输入类的dom时,会产生错误的dom更新。
    3. 使用index和其他值拼接作为key时,和使用index作为key类似,每次的key都是不一样的,所以没办法做到增加效率。
    4. 再开发中推荐使用集合中固定的唯一标识作为key,例如唯一的id或者codeNumber等。
    5. 当不存在unshift的添加或者shift删除的时候可以使用index作为key,不会有什么影响,但是不推荐,万一养成习惯就不好改正了。

    下面讲针对结论1和2进行简单的讲述(前提: 需要已经掌握了vue3的diff算法, 本文中没有对diff算法做解释,所以之前没有看过或者了解过diff算法的可能不适合本篇文章)。

    1. 逆序添加或者删除的时候,即在集合头部添加或者删除, 会使所有new key和old key不一致,就会产生所有的dom重新渲染的情况,和没有key的效率没有啥区别。 

    2. 同时使用index作为key还有产生一些bug,当使用输入类的dom时,会产生错误的dom更新

    先看一下没有绑定key的情况。 

    html:

    1. <ul class="flex" v-for="item in state.tableData1">
    2. <li>{{item.id}}li>
    3. <li class="ml-20">{{item.name}}li>
    4. <input class="ml-10" />
    5. <el-input class="width-200 ml-10" v-model="item.inputValue" placeholder="请输入">el-input>
    6. ul>
    7. <el-button @click="onTestClick">测试el-button>

    js部分代码:

    1. const state = reactive({
    2. tableData1: [{
    3. id: 1,
    4. name: 'james1',
    5. inputValue: ''
    6. },{
    7. id: 2,
    8. name: 'james2',
    9. inputValue: ''
    10. },{
    11. id: 3,
    12. name: 'james3',
    13. inputValue: ''
    14. }]
    15. })
    16. const onTestClick = () => {
    17. state.tableData1.unshift({ id: state.tableData1.length + 1, name: `james${state.tableData1.length + 1}`, inputValue: '' })
    18. }

    在输入框里输入相应的值之后,界面效果如下:

    点击测试按钮之后,界面效果如下图所示;

    由于elementpuls组件的v-model的原因,所有的dom节点在错位后会根据具体的值重新赋值, 但是原生的输入框没有双向绑定,所以就会保持原始的dom节点上的值,由此可见,如果没有双向绑定就会出现值的错误,有双向绑定之后,由于所有的dom重新渲染赋值,界面的效率会受到影响。

    下面来看一下使用index作为key会不会也有这种情况:

    HTML

    1. <ul class="flex" v-for="(item, index) in state.tableData1" :key="index">
    2. <li>{{item.id}}li>
    3. <li class="ml-20">{{item.name}}li>
    4. <input class="ml-10" />
    5. <el-input class="width-200 ml-10" v-model="item.inputValue" placeholder="请输入">el-input>
    6. ul>
    7. <el-button @click="onTestClick">测试el-button>

    JS部分保持和上文的js一致 

    点击测试按钮后效果图如下:

     和没有绑定key时一样的问题。

    下面来看一下绑定了唯一固定的值作为key的情况

    HTML

    1. <ul class="flex" v-for="item in state.tableData1" :key="item.id">
    2. <li>{{item.id}}li>
    3. <li class="ml-20">{{item.name}}li>
    4. <input class="ml-10" />
    5. <el-input class="width-200 ml-10" v-model="item.inputValue" placeholder="请输入">el-input>
    6. ul>
    7. <el-button @click="onTestClick">测试el-button>

    效果如下:

    附件:diff算法的简单梳理:

    1. 经过上述我们大致知道了diff算法的流程
      1 从头对比找到有相同的节点 patch ,发现不同,立即跳出。

      2如果第一步没有patch完,立即,从后往前开始patch ,如果发现不同立即跳出循环。

      3如果新的节点大于老的节点数 ,对于剩下的节点全部以新的vnode处理( 这种情况说明已经patch完相同的vnode )。

      4 对于老的节点大于新的节点的情况 , 对于超出的节点全部卸载 ( 这种情况说明已经patch完相同的vnode )。

      5不确定的元素( 这种情况说明没有patch完相同的vnode ) 与 3 ,4对立关系。

      1 把没有比较过的新的vnode节点,通过map保存
      记录已经patch的新节点的数量 patched
      没有经过 path 新的节点的数量 toBePatched
      建立一个数组newIndexToOldIndexMap,每个子元素都是[ 0, 0, 0, 0, 0, 0, ] 里面的数字记录老节点的索引 ,数组索引就是新节点的索引
      开始遍历老节点
      ① 如果 toBePatched新的节点数量为0 ,那么统一卸载老的节点
      ② 如果,老节点的key存在 ,通过key找到对应的index
      ③ 如果,老节点的key不存在
      1 遍历剩下的所有新节点
      2 如果找到与当前老节点对应的新节点那么 ,将新节点的索引,赋值给newIndex
      ④ 没有找到与老节点对应的新节点,卸载当前老节点。
      ⑤ 如果找到与老节点对应的新节点,把老节点的索引,记录在存放新节点的数组中,
      1 如果节点发生移动 记录已经移动了
      2 patch新老节点 找到新的节点进行patch节点
      遍历结束

      如果发生移动
      ① 根据 newIndexToOldIndexMap 新老节点索引列表找到最长稳定序列
      ② 对于 newIndexToOldIndexMap -item =0 证明不存在老节点 ,从新形成新的vnode
      ③ 对于发生移动的节点进行移动处理。

     diff算法引自:vue3.0 diff算法详解(超详细)_zl_Alien的博客-CSDN博客_vue3的diff算法

  • 相关阅读:
    Linux yum
    电子与通信专业英语(第2版)Electronic Components课文补充
    Vue-cli搭建SPA项目
    推荐系统-排序层-模型(一):Embedding + MLP(多层感知机)模型【Deep Crossing模型:经典的Embedding+MLP模型结构】
    【计算机网络】网络原理
    Kettle入门到实战
    <四>理解空间配置器allocator, 优化STL 中的Vector
    React(6)-类组件的setState异步问题
    互联网“凛冬已至”,这套Java面试突击宝典助你破局,直击大厂
    JVM的运行时数据区
  • 原文地址:https://blog.csdn.net/James_liPeng/article/details/127632583