uni-app中scroll-view内lazy-load无效,因平台限制仅对页面级滚动生效;应改用uni.createintersectionobserver监听视口,注意节点需真实渲染且$nexttick后observe,小程序threshold须为数组。

uni-app 图片懒加载不是“开了 lazy-load 就万事大吉”,它在 scroll-view 里根本不起作用;长列表卡顿也不是因为数据多,而是你让 Vue 渲染了所有项、key 写成了 index、图片没裁剪还堆在一起解码。
scroll-view 里 lazy-load 为什么无效
微信小程序和部分 App 端的 <image></image> 组件,lazy-load 属性只对页面级滚动(onPageScroll)生效,对 scroll-view 容器内的滚动完全不监听。这不是 bug,是平台限制。
- 现象:列表塞进
scroll-view,加了lazy-load="true",滑动时所有图片仍一次性请求 - 正确做法:改用
uni.createIntersectionObserver手动监听每个image是否进入视口 - 注意点:观察目标必须是真实渲染后的 DOM 节点(
this.$refs.imgItem),且要在this.$nextTick后执行observe,否则节点未挂载会失败 - 小程序端
threshold只接受数组(如[0, 0.1, 0.5]),设成数字如0.2会静默失败;H5 端才支持单数值
长列表里图片怎么真正“按需加载”
光靠懒加载不够——如果图片本身是 5MB 原图,哪怕延迟加载,用户一滑到就卡死。关键得先压缩再懒加载。
- CDN 动态裁剪优先:用阿里云 OSS、腾讯云 COS 等,URL 加参数如
?x-oss-process=image/resize,w_200/format,webp,把原图压到几 KB - 本地图片或非 CDN 图,统一走工具函数处理:
resizeImg(url, 200),避免硬编码 URL 参数 - 所有
<image></image>必须设固定宽高或mode(如mode="widthFix"),否则加载前无法占位,导致布局跳动(layout thrashing) - 加占位样式:
.img-placeholder { background-color: #f5f5f5; transition: opacity 0.3s; },防止白屏闪动
scroll-view 滚动监听失效的三个硬条件
很多人写了 bindscroll 却收不到事件,不是代码错,是没同时满足这三条:
-
scroll-y="true"必须显式写上,不能靠 CSS 或父级推导 - 父容器要有固定高度(如
height: 600rpx),用min-height或flex: 1不行 - 不能启用
enable-back-to-top,它会劫持滚动事件,导致bindscroll失效 - H5 端没问题,但小程序(尤其 iOS 微信)有节流,高频滚动会丢事件,建议加防抖或直接切到页面级
onPageScroll
虚拟滚动必须知道的底层约束
uni-app 没内置虚拟滚动,所谓“虚拟列表”全是基于 scroll-view + scrollTop 手算索引实现的,它不自动管理 ref,也不感知 item 高度变化。
- 必须预设固定行高(比如
80rpx),用Math.floor(scrollTop / 80)算起始 index - 只渲染可视区域 ± 缓冲区(如前后各 5 条),其余用空
<view></view>占位,高度由height: calc(80rpx * 总数)撑开滚动条 - 禁用
:key="index",必须用稳定字段如:key="item.id",否则增删时状态错乱 - iOS 卡顿常见于内部用了
transform、opacity或多层flex,会强制 CPU 渲染,要扁平化结构
最常被忽略的一点:图片懒加载和虚拟滚动不是二选一,而是必须叠加使用。懒加载管资源请求数量,虚拟滚动管 DOM 节点数量,两者缺一不可——否则哪怕只加载 20 张图,若背后 v-for 渲染了 1000 条,小程序照样白屏。











