虚拟滚动中图片懒加载需适配动态渲染:每次 renderItem 时用全局 IntersectionObserver 立即 observe 新 img,设置 rootMargin 与 bufferSize 一致、threshold=0.1 预加载,并在节点移除前 unobserve,配合 loading="lazy" 和固定宽高防抖动。

在虚拟滚动列表中实现图片懒加载,关键不是“叠加两种技术”,而是让懒加载逻辑适配虚拟滚动的动态渲染机制——因为虚拟列表本身只挂载视口内少量 DOM,传统基于 scroll 事件或全量节点观察的方式会失效或漏触发。
确保懒加载监听范围覆盖动态插入的节点
虚拟列表每次滚动都会销毁旧节点、创建新节点。如果用 IntersectionObserver 在初始化时一次性 observe 所有图片,那些后续动态插入的图片根本不会被监听到。
- 每次渲染新项(比如 renderItem 函数执行时),立即对新生成的
调用observer.observe(img) - 避免在组件 mount 时批量 observe —— 那些还没创建的 img 标签并不存在,observe 无效
- 若使用 React/Vue 等框架,把 observer 实例挂载在父容器或全局状态中,而非依赖局部生命周期
用 IntersectionObserver 的 threshold 和 rootMargin 精准预加载
虚拟滚动本身已有缓冲区(buffer),图片懒加载要与之协同,不能等图片刚进视口才加载,否则会看到占位图闪动。
- 设置
rootMargin: '100px',让图片在距离视口上方/下方 100px 时就开始加载,匹配常见缓冲区高度 - 配合
threshold: 0.1(10% 可见即触发),比threshold: 0更早响应,减少空白感 - 注意:rootMargin 值需与你虚拟列表的 bufferSize 一致,否则预加载过早浪费资源,过晚影响体验
避免重复 observe 和内存泄漏
每个 item 渲染时都 new Observer 会造成严重性能问题;而只用一个 observer 实例,又必须处理节点销毁时的 unobserve。
- 统一维护一个全局
IntersectionObserver实例,在组件卸载或列表重置时调用observer.disconnect() - 每次插入新
后 observe,同时在该 img 被移出 DOM 前(如回收池清理时)调用observer.unobserve(img) - 不要依赖
img.onload后自动 unobserve —— 虚拟列表可能已把该 img 移除,导致 unobserve 失败或报错
降级兼容与占位策略要同步虚拟滚动节奏
loading="lazy" 在虚拟列表里基本无效(浏览器无法感知动态插入的 img),所以必须靠 JS 控制;但可作为基础 fallback。
- 所有图片默认写
loading="lazy"+src="placeholder.svg",既满足 SEO 又提供视觉锚点 - data-src 存真实地址,JS 加载成功后才赋值给 src,并移除占位类:
img.classList.remove('loading') - 为防止滚动过程中因加载延迟导致布局抖动,给 img 设置固定宽高或 aspect-ratio,让虚拟容器高度计算不受图片尺寸影响
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











