图片懒加载实现汇总
实现方案一:scroll + getBoundingClientRect
监听
scroll/resize(必要时监听orientationchange)对每张未加载图片做一次可视区域判断:
img.getBoundingClientRect()当
rect.top < viewportHeight + preload时触发加载
代码示例
HTML:
<img
class="lazy"
src="placeholder.png"
data-src="https://example.com/big-1.jpg"
width="320"
height="180"
alt="demo"
/>JS:
const preloadPx = 200; // 提前 200px 预加载,避免滚动到可视区才开始下载
function inViewport(el) {
const rect = el.getBoundingClientRect();
const vh = window.innerHeight || document.documentElement.clientHeight;
// rect.top < vh + preload:元素顶部进入“预加载区”
// rect.bottom > 0:元素没有完全滚出上方(可选,避免反复触发)
return rect.top < vh + preloadPx && rect.bottom > -preloadPx;
}
function loadImg(img) {
const realSrc = img.getAttribute("data-src");
if (!realSrc) return;
// 防止重复加载
img.setAttribute("data-loaded", "1");
img.src = realSrc;
img.addEventListener(
"error",
() => {
// 失败兜底:替换为错误占位图,避免一直空白
img.src = "error.png";
},
{ once: true }
);
}
function createScrollLazyLoader() {
let ticking = false;
const handler = () => {
if (ticking) return;
ticking = true;
// 合并到下一帧,避免滚动事件高频触发造成的卡顿
requestAnimationFrame(() => {
const imgs = Array.from(document.querySelectorAll("img.lazy"))
// 只处理未加载的
.filter((img) => img.getAttribute("data-loaded") !== "1");
imgs.forEach((img) => {
if (inViewport(img)) loadImg(img);
});
// 全部加载完就解绑,避免无意义监听(面试加分点)
const remain = document.querySelector(
'img.lazy:not([data-loaded="1"])'
);
if (!remain) {
window.removeEventListener("scroll", handler);
window.removeEventListener("resize", handler);
}
ticking = false;
});
};
// 初始化先跑一次,避免首屏图片不加载
handler();
window.addEventListener("scroll", handler, { passive: true });
window.addEventListener("resize", handler);
}
createScrollLazyLoader();实现方案二:IntersectionObserver
核心思路
某个元素是否与根容器(默认视口)产生交叉。
function createIOLazyLoader() {
const imgs = Array.from(document.querySelectorAll("img.lazy"));
// rootMargin 用于“提前加载”,等价于把视口向外扩一圈
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (!entry.isIntersecting) return;
const img = entry.target;
const realSrc = img.getAttribute("data-src");
if (!realSrc) {
observer.unobserve(img);
return;
}
// 标记已处理,避免某些场景下重复赋值
img.setAttribute("data-loaded", "1");
img.src = realSrc;
img.addEventListener(
"error",
() => {
img.src = "error.png";
},
{ once: true }
);
observer.unobserve(img);
});
},
{
root: null, // 视口
rootMargin: "200px 0px", // 上下提前 200px
threshold: 0.01, // 进入一点点就触发
}
);
imgs.forEach((img) => observer.observe(img));
}实现方案三:loading="lazy"
<img
src="https://example.com/big-2.jpg"
loading="lazy"
width="320"
height="180"
alt="native lazy"
/>图片懒加载实现汇总
http://localhost:8090/archives/tu-pian-lan-jia-zai-shi-xian-hui-zong