虚拟列表是小程序长列表的必需方案,因平台限制和Vue响应式开销导致直接v-for渲染数百条数据必卡顿;需用scroll-view+固定高度+手动计算可视范围实现,禁用vue-virtual-scroller。

直接用 v-for 渲染几百条以上数据,小程序端大概率白屏或滚动卡成幻灯片——这不是代码写得烂,而是平台限制+Vue响应式开销双重暴击。虚拟列表不是“可选优化”,而是长列表在小程序里能跑起来的底线。
为什么 uni-app 不能直接用 vue-virtual-scroller
这个库依赖 document 和 CSS transform 做位移控制,但 uni-app 编译到微信小程序时根本没有 DOM,App 端走的是原生渲染层,H5 端虽能跑但样式常错乱。强行引入会报 document is not defined,或者滚动时 item 错位、高度塌陷。别试,没用。
scroll-view + 手动计算可视范围是唯一靠谱路径
核心就三件事:固定容器高度、监听 scroll-top、动态截取 data 子集。所有其他“自动”方案(比如靠 uni.createSelectorQuery() 测高)在真机上都不可靠——它异步、慢、多次调用会累积延迟,且 iOS 下频繁触发 layout 会导致掉帧。
-
scroll-view必须设固定height(如600rpx),且scroll-y="true",否则scroll-top不更新 -
itemHeight必须是固定值(比如120rpx),不能用min-height或 flex 自适应,否则计算偏移量会漂移 - 可视起始索引用
Math.floor(scrollTop / itemHeight),结束索引加缓冲区(建议 +2~3 条),避免快速滚动留白 - 偏移量用
getOffset = startIdx * itemHeight,然后给外层view加transform: translateY(${offset}px) - 每次
handleScroll里别直接赋值this.visibleData = ...,包一层this.$nextTick(() => { ... })防 setData 冲突
key 和列表项结构比算法还关键
很多人写了虚拟滚动逻辑,还是卡——问题八成出在 key 或样式上。小程序里 DOM 复用机制比 H5 更敏感,错一点就全崩。
-
:key必须是稳定唯一值,比如item.id;禁用:key="index",否则增删项时组件状态会串 - 列表项内禁用
watch、深层computed、或每个item是个带 10+ 字段的响应式对象——这会让 Vue 跟踪开销爆炸 - 样式尽量扁平:
display: flex嵌套别超过两层,避免transform、opacity,iOS 会强制 CPU 渲染 - 图片必须加
lazy-load属性,并显式设width/height,否则加载时重排 layout,滚动直接抖三抖
真正卡顿的根源往往不在“怎么算可视区域”,而在于 key 写错、item 高度不固定、或图片没懒加载——这些细节一漏,再精巧的虚拟滚动逻辑也救不回来。



















