记一次v-if和key错误使用,导致vue2的内存爆炸修复!

发布于:2025-08-04 ⋅ 阅读:(12) ⋅ 点赞:(0)

之前写了一个功能,通过v-for渲染数据,但是数据中存在hidden项,则对数据进行隐藏,由于数据显隐采用了v-if,且循环使用的key,用了(index+900)之类的写法,导致虚拟dom的计算量成倍上涨。
具体问题如下:

<template v-if="showFieldList">
   <template v-for="(item, index) in fieldList">
     <template v-if="!item.hidden">
       <!-- 单选框 -->
       <el-col
         :span="item.span || 8"
         :key="index + 900"
         v-if="item.type === 'radio'"
       >
   ......
   </template>
  </template>
</template>

这里犯了两个重大的错误,
第一,key作为vue虚拟dom中递归判断改变的标志,正确的使用key可以节省很多无畏的性能损失。

设置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效率

第二,Vue 官方强烈不建议在 v-for 内使用 v-if,因为这会 导致重复计算。改用 computed 过滤数据:

<!-- 错误做法 -->
<div v-for="item in list" v-if="item.isActive">{{ item.text }}</div>

<!-- 正确做法 -->
<div v-for="item in activeList" :key="item.id">{{ item.text }}</div>

<script>
export default {
  computed: {
    activeList() {
      return this.list.filter(item => item.isActive);
    }
  }
};
</script>

最后修改为如下写法:

<template v-if="showFieldList">
  <template v-for="(item, index) in activeList">
     <template>
       <!-- 单选框 -->
       <el-col
         :span="item.span || 8"
         :key="item.code"
         v-if="item.type === 'radio'"
       >
   ......
   </template>
  </template>
</template>

activeList() {
  return this.fieldList.filter(item => !item.hidden);
}

至此,性能问题基本解决。引以为鉴!


网站公告

今日签到

点亮在社区的每一天
去签到