Vue 项目报表列表大体积下拉框性能优化问题咨询
核心卡顿原因
- 你当前用
v-show控制设置面板显隐,面板内部的select和上千个option就算隐藏也会全部渲染到DOM中,如果报表数量多,会一次性生成几万甚至十万级的冗余DOM节点,占用大量内存导致卡顿 - 所有
v-for未指定唯一key,Vue diff比对时开销成倍增加 similarworksheets是普通方法,每次组件重渲染都会重复执行过滤逻辑,无缓存机制
可落地优化方案(基本保留原有交互结构)
方案1:最小改造成本优化(改动量<10行)
- 将设置面板的
v-show替换为v-if,只有点击设置按钮展开面板时才会渲染内部的下拉及选项,未展开的报表不会生成冗余DOM,初始加载性能直接提升90%以上 - 所有
v-for添加唯一key,不要用索引作为key - 用缓存函数改造
similarworksheets,避免重复计算
改造后代码示例:
<div class="list" v-for="(report,repidx) in filteredReports" v-show="hideDemo.indexOf(report.Name) == -1" :key="report.id"> <h2>{{report.Name}}</h2> <button @click="report.showsettings=true">Settings</button> <!-- 替换v-show为v-if --> <div v-if="report.showsettings"> <select name="worksheet" v-model="report.worksheet"> <optgroup label="similarworksheets"> <option v-for="sm in cachedSimilarWorksheets(report.worksheet)" :key="sm.id">{{sm.Name}}</option> </optgroup> <optgroup label="allworksheets"> <option v-for="worksheet in allworksheets" :key="worksheet.id">{{worksheet.Name}}</option> </optgroup> </select> </div> </div>
js部分缓存函数实现(Vue2/Vue3通用):
import { memoize } from 'lodash' // 初始化时就生成带缓存的相似工作表计算函数 created() { this.cachedSimilarWorksheets = memoize((currentWorksheet) => { // 原有的similarworksheets过滤逻辑 return this.allworksheets.filter(item => /* 相似匹配规则 */) }) } // 如果allworksheets会更新,记得在更新时清空缓存 // this.cachedSimilarWorksheets.cache.clear()
方案2:性能最优方案(完全解决卡顿,交互无感知)
全局只保留1个下拉弹窗组件,点击任意报表的设置按钮时,将当前点击的report实例传入弹窗,选完值后直接回写到对应report.worksheet即可,整个页面最多只渲染1份下拉选项DOM,1000条数据也不会有任何卡顿。
示例实现:
<!-- 报表列表部分 --> <div class="list" v-for="(report,repidx) in filteredReports" v-show="hideDemo.indexOf(report.Name) == -1" :key="report.id"> <h2>{{report.Name}}</h2> <button @click="openSettings(report)">Settings</button> </div> <!-- 全局共用的设置弹窗,全局只渲染一次 --> <div v-if="visibleSettings" class="settings-modal"> <select name="worksheet" v-model="currentReport.worksheet"> <optgroup label="similarworksheets"> <option v-for="sm in cachedSimilarWorksheets(currentReport.worksheet)" :key="sm.id">{{sm.Name}}</option> </optgroup> <optgroup label="allworksheets"> <option v-for="worksheet in allworksheets" :key="worksheet.id">{{worksheet.Name}}</option> </optgroup> </select> <button @click="visibleSettings = false">确认</button> </div>
js部分:
data() { return { visibleSettings: false, currentReport: null } }, methods: { openSettings(report) { this.currentReport = report this.visibleSettings = true } }
额外优化建议
如果1000条选项展开时依然有卡顿,可以给下拉组件添加虚拟滚动能力,只渲染可视区内的10-20个选项,不管数据量多大都不会有渲染压力,主流UI组件库(ElementUI、Ant Design Vue等)的Select组件都自带虚拟滚动配置,直接开启即可。
内容的提问来源于stack exchange,提问作者Gotts
相关产品推荐
相关产品推荐

