uni-app图片懒加载不能仅依赖lazy-load属性,因其在H5端默认失效、小程序行为不一致、瀑布流中失灵,需结合IntersectionObserver手动控制可视区判断与预设高度防抖动。

uni-app 的图片懒加载不能只靠 lazy-load 属性一招鲜——它在 H5 端默认不生效,小程序端行为不一致,瀑布流里基本失效,必须配合可视区判断手动控制。
为什么 lazy-load 在很多场景下没用
这个属性是 image 组件的原生支持,但平台兼容性差:lazy-load 在微信小程序中仅对垂直滚动生效,H5 端需额外配置(如设置 decode="async" 或启用 IntersectionObserver),App 端部分机型直接忽略。更关键的是,它无法感知“是否真正进入可视区”——比如 scroll-view 内部滚动、列表嵌套、或使用了 transform 平移的容器,lazy-load 就会完全失灵。
- 不是所有平台都支持,尤其 H5 必须手动 fallback
- 不适用于
scroll-view嵌套结构(父容器非页面级滚动) - 无法控制预加载距离(比如提前 200px 加载)
- 加载时机不可控,和骨架屏、图片尺寸变化冲突
用 uni.createIntersectionObserver 替代滚动监听
比 bindscroll 更准、更轻量,且能跨平台工作。它不依赖滚动事件节流,也不受 iOS scroll-top 不准的影响,适合图片逐个触发加载。
- 观察目标必须是真实渲染后的 DOM 节点,
this.$refs.imgItem要等this.$nextTick()后再调用observe() - 不要一次性给全部图片创建 observer,按需创建(比如首次渲染只观察前 10 项)
- 可传入
threshold控制触发比例,例如[0, 0.1, 0.5]表示 0% / 10% / 50% 进入时都可响应 - 每次
observe()后记得disconnect(),否则内存泄漏
const observer = uni.createIntersectionObserver(this)
observer.relativeTo('.scroll-container').relativeToViewport({ top: 200 })
observer.observe(`#img-${index}`, (res) => {
if (res.intersectionRatio > 0) {
this.loadImage(index)
observer.disconnect()
}
})
瀑布流中图片懒加载必须预知高度
滚动抖动、回流、重复加载的根本原因,是图片未占位导致 layout 反复重排。uni-app 没有自动 aspect-ratio 回退机制,uni.getImageInfo 是唯一可靠方案。
- 服务端返回图片宽高字段(推荐),或客户端用
uni.getImageInfo预加载,拿到后动态设:style="{ height: item.height + 'px' }" - 绝对不要用
v-if控制图片显隐——会销毁重建,改用:style="{ opacity: loaded ? 1 : 0 }"+ CSS transition - 瀑布流分列必须用二维数组(如
columns: [[], [], []]),否则局部更新会触发整列重渲染 - 避免在
bindscroll里直接发请求,先用getBoundingClientRect()判断底部 item 是否进视口
滚动容器选 scroll-view 还是页面级 onPageScroll
取决于布局层级和平台:页面级滚动用 onPageScroll 更稳,scroll-view 适合局部区域但限制多。
-
scroll-view必须同时满足三要素:固定高度(如height: 600rpx)、scroll-y="true"、绑定bindscroll,缺一不可 - 微信小程序中
bindscroll有高频丢帧问题,建议加 16ms 防抖,或改用onPageScroll+querySelector查容器 offsetTop - 安卓真机上
scroll-top值可能不准,优先用uni.createSelectorQuery().select('.item').boundingClientRect()获取真实位置 - 如果滚动区域是全屏页面主体,直接用
onPageScroll,性能和兼容性都更好
真正难的不是“怎么加载”,而是“怎么不让它重复加载、跳动、卡顿”。图片尺寸不确定、滚动事件不准、observer 创建时机错乱——这些细节不处理,再好的逻辑也会在真机上崩掉。


















