IntersectionObserver 是懒加载唯一推荐方案,它原生支持、不阻塞主线程、自动处理边界情况;SPA中需对新节点重新 observe,且必须在真正需要时才设置 img.src 以避免重复请求。

懒加载的核心判断依据是 IntersectionObserver
不用监听 scroll 事件手动计算元素位置,现代浏览器原生支持的 IntersectionObserver 是唯一推荐方案。它在元素进入视口时触发回调,不阻塞主线程,且能自动处理滚动、缩放、iframe 内嵌等边界情况。
常见错误是 fallback 到 getBoundingClientRect() + scroll 监听 —— 这会导致频繁重排重绘,尤其在低端安卓机上卡顿明显。只有需要兼容 IE 或极老 Android(
-
rootMargin设为"100px"可提前 100px 加载,避免用户快速滚动时出现空白 - 不要给
threshold设过小的值(如0.01),会增加触发频率;一般[0, 0.1, 0.5]足够覆盖多数场景 - 观察器实例应复用,避免每个图片都新建一个
IntersectionObserver
图片懒加载必须配合 loading="lazy" 和占位策略
loading="lazy" 是 HTML 原生懒加载属性,对 和 有效,但仅适用于常规图片请求(不支持背景图、CSS 雪碧图等)。它和 JS 懒加载不是互斥关系,而是互补:原生属性由浏览器控制,JS 方案负责兜底和精细控制。
没设置占位符直接留空 src,会导致布局抖动(layout shift)。正确做法是:
立即学习“Java免费学习笔记(深入)”;
- 用
data-src存真实地址,src设为 1×1 透明 base64 占位图:"data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" - 加载完成前加
opacity: 0,成功后用 CSS 过渡淡入,避免突兀出现 - 失败时显示 fallback 图并记录错误:
img.addEventListener("error", () => img.src = "/fallback.jpg")
IntersectionObserver 在单页应用(SPA)中要重新 observe 新渲染的节点
Vue/React 等框架动态插入 DOM 后,IntersectionObserver 不会自动跟踪新节点。常见错误是只在页面初始化时调用一次 observer.observe(),后续路由切换或列表刷新后新图片完全不触发加载。
解决方式取决于框架机制:
- React 中,在
useEffect里对每次渲染的新 ref 调用observer.observe(el),卸载时调用observer.unobserve(el) - Vue 3 的
onMounted+onBeforeUnmount同理;若用v-for渲染列表,确保每个都有独立 ref 或使用querySelectorAll批量 observe - 纯 JS SPA:监听
MutationObserver捕获新增的img[data-src]节点,再交给IntersectionObserver管理
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.classList.remove("lazy");
observer.unobserve(img); // 加载完立即解绑,避免重复触发
}
});
}, {
rootMargin: "100px",
threshold: [0, 0.1]
});
document.querySelectorAll("img[data-src]").forEach(img => observer.observe(img));
真正容易被忽略的是资源加载顺序与缓存协同:懒加载图片的 src 设置时机,会影响浏览器是否复用已缓存的资源。如果先设 src 再加 class 控制样式,可能触发两次请求;必须确保只在需要时才写入 src 属性。











