Vue JS 循环渲染大量商品项的性能优化技术咨询
核心问题原因
你遇到的卡顿本质是无限滚动加载后DOM节点和Vue组件实例大量堆积:加载1250件商品就意味着同时存在1250个商品组件实例和对应DOM节点,Vue需要维护所有组件的响应式绑定,路由切换时要批量销毁上千个节点和实例,这就是4-6秒延迟的核心来源。
优化方案
1. 优先使用虚拟列表(收益最高)
这是大列表场景的标准解决方案,无论总数据量多大,始终只渲染可视区域上下2-3屏的DOM节点,滚动时复用现有DOM替换内容,不会出现DOM堆积问题。
- Vue2项目可以用
vue-virtual-scroller组件,Vue3项目可以用vueuse提供的useVirtualList工具,也可以直接用UI组件库自带的虚拟列表组件 - 改造成本极低:仅需要把现有v-for的部分替换为虚拟列表组件,传入全量商品数据、配置每一项的固定高度、复用你现有的
item-card/item-card-for-mobile组件作为渲染模板即可,原有业务逻辑完全不用修改
2. 现有无限滚动加DOM卸载逻辑(过渡方案)
如果暂时不想重构为虚拟列表,可以给现有滚动逻辑加不可见区域DOM卸载规则,把DOM数量控制在百级以内:
- 把商品按每30件分为一组,给每组加可见性监听(可以直接复用你现有的
vue-observe-visibility能力) - 不在可视区域上下2屏范围内的分组直接用
v-if=false卸载DOM,仅保留商品数据在内存中,需要展示时再重新渲染
3. 现有代码细节优化
不需要改核心逻辑的前提下,做以下优化也能大幅降低性能开销:
- 把设备类型判断移到v-for外层,避免循环内每一项都重复判断
mobile变量,优化后代码示例:
<template v-if="!mobile"> <div v-for="value in filteredRecords.slice(0, showCount)" :key="value.id"> <item-card :product_id="value.id" :product_slug="value.product_slug" :product_name="value.product_name" :product_img="getImage(value.product_img , '300x300')" :product_price="value.product_price" :product_sale_price="value.product_sale_price" :product_stock="value.product_stock" :locale="locale" :heart="true" /> </div> </template> <template v-else> <div v-for="value in filteredRecords.slice(0, showCount)" :key="value.id"> <item-card-for-mobile :product_id="value.id" :product_slug="value.product_slug" :product_name="value.product_name" :product_img="getImage(value.product_img , '300x300')" :product_price="value.product_price" :product_sale_price="value.product_sale_price" :product_stock="value.product_stock" :locale="locale" :quickview="true" /> </div> </template>
- 给
getImage函数加缓存,相同入参直接返回结果,避免每次渲染都重复计算 - 开启商品图片懒加载,仅加载可视区域内的图片,降低内存占用
- Vue2可以把商品组件改为函数式组件,Vue3可以用
markRaw包裹不需要响应式的商品属性、或者给组件开启shallow模式,减少响应式绑定开销
4. 路由切换优化
如果做完上述优化还有延迟,可以加主动清屏逻辑:
- 监听路由离开钩子,离开列表页前先把整个商品列表容器的
v-if设为false,主动清空所有商品DOM后再执行路由跳转,配合简单的loading过渡动画即可完全隐藏销毁耗时
内容的提问来源于stack exchange,提问作者rocket_moon
相关产品推荐
相关产品推荐

