Vue中v-for内v-if条件渲染偶发卡顿问题排查与优化咨询
问题分析与解决方案
核心问题排查
你的代码存在几个关键问题,导致了后续操作的偶发卡顿:
图标状态与项状态不匹配:
展开按钮的图标绑定了全局的listItemExpanded变量,但实际切换的是每个item自身的expanded属性,这会导致图标显示与实际展开状态不一致,同时可能触发不必要的全局重渲染。响应性缺失:
如果actors数组中的对象初始没有expanded属性,直接通过item.expanded = !item.expanded添加属性时,Vue 无法检测到这个新增属性的变化,导致渲染不及时或卡顿。低效的列表渲染:
你在每个分组下遍历所有actors,再通过v-if筛选匹配的项,这种方式会让 Vue 渲染大量不必要的元素,当数据量较大时必然导致性能问题。缺少唯一标识(key):
外层的<template v-for="item in actors">没有设置:key,Vue 无法精准跟踪每个列表项的状态,会导致频繁的 DOM 复用与重渲染,引发卡顿。
分步修复方案
1. 修正图标状态绑定
将图标切换逻辑改为使用当前项的 expanded 属性:
<v-icon> {{ item.expanded ? "mdi-chevron-up" : "mdi-chevron-down" }} </v-icon>
2. 确保 expanded 属性的响应性
有两种方式解决:
- 初始化时添加属性:在获取或创建
actors数组时,为每个对象预添加expanded: false:// 示例:从接口获取数据后处理 this.actors = fetchedActors.map(actor => ({ ...actor, expanded: false })); - 使用 Vue.set 动态添加:如果无法提前初始化,修改展开方法:
import Vue from 'vue'; expandListItem(item) { Vue.set(item, 'expanded', !item.expanded); }
3. 预分组优化渲染效率
添加计算属性提前按分组整理演员数据,避免重复遍历:
computed: { groupedActors() { return this.actors.reduce((result, actor) => { const groupKey = actor.relation.toLowerCase(); if (!result[groupKey]) result[groupKey] = []; result[groupKey].push(actor); return result; }, {}); } }
然后修改模板,直接遍历预分组后的对应数据:
<v-list> <div v-for="group in groups" :key="group"> <h4 class="ml-4 my-2">{{ group }}</h4> <!-- 直接遍历当前分组的演员,无需额外筛选 --> <template v-for="item in groupedActors[group.toLowerCase()] || []" :key="item.id"> <v-hover v-slot="{ hover }"> <v-card :elevation="hover ? 6 : 2" class="mx-4 my-2"> <v-list-item> <!-- 保留原有列表项内容 --> </v-list-item> </v-card> </v-hover> </template> </div> </v-list>
4. 添加正确的 key 属性
确保所有 v-for 循环都设置唯一的 key(如 item.id),帮助 Vue 高效跟踪元素状态。
替代方案:使用 Vuetify 内置展开面板
如果不需要完全自定义样式,Vuetify 的 v-expansion-panel 组件已经针对展开/收起场景做了性能优化,代码更简洁:
<v-expansion-panels> <div v-for="group in groups" :key="group"> <h4 class="ml-4 my-2">{{ group }}</h4> <v-expansion-panel v-for="item in groupedActors[group.toLowerCase()] || []" :key="item.id" > <v-expansion-panel-header> <v-list-item> <v-list-item-content> <v-list-item-title>{{ item.name }}</v-list-item-title> </v-list-item-content> <v-list-item-action> <UiDotMenu :actions="listItemActions" :payload="item" @removeActor="removeActor" /> </v-list-item-action> </v-list-item> </v-expansion-panel-header> <v-expansion-panel-content> Show this content </v-expansion-panel-content> </v-expansion-panel> </div> </v-expansion-panels>
内容的提问来源于stack exchange,提问作者Ash
相关产品推荐
相关产品推荐

