查看: 100|回复: 1

JavaScript图片懒加载实战:基于IntersectionObserver API的完整实现

[复制链接]
发表于 1 小时前 | 显示全部楼层 |阅读模式
在现代前端开发中,图片懒加载是提升页面加载速度和用户体验的常用手段。传统的懒加载方案多依赖 scroll 事件监听或第三方库,性能开销较大且实现复杂。本文基于原生 IntersectionObserver API 实现一套完整、高性能的图片懒加载方案,无需引入任何外部依赖,并附带可运行的完整代码。
  1. // 配置项
  2. const TOTAL_ITEMS = 99;
  3. const DEFAULT_IMG = 'https://pica.zhimg.com/v2-f052aa50ca65df4bad1c3b7e4084d00e_1440w.jpg';
  4. const IMG_URL_TEMPLATE = (index) => `https://picsum.photos/400/600?r=${index}`;
  5. const cardList = document.querySelector('.card-list');
  6. // 生成图片卡片 - 使用 DocumentFragment 批量操作 DOM
  7. function generateItems() {
  8.   const fragment = document.createDocumentFragment();
  9.   for (let i = 0; i < TOTAL_ITEMS; i++) {
  10.     const div = document.createElement('div');
  11.     div.classList.add('item');
  12.     const img = document.createElement('img');
  13.     img.src = DEFAULT_IMG;
  14.     img.dataset.src = IMG_URL_TEMPLATE(i);
  15.     img.alt = `Image ${i + 1}`;
  16.     div.appendChild(img);
  17.     fragment.appendChild(div);
  18.   }
  19.   cardList.appendChild(fragment);
  20. }
  21. // 初始化 IntersectionObserver
  22. function initLazyLoad() {
  23.   const observer = new IntersectionObserver(
  24.     (entries, observer) => {
  25.       entries.forEach((entry) => {
  26.         if (!entry.isIntersecting) return;
  27.         const img = entry.target;
  28.         img.src = img.dataset.src;
  29.         observer.unobserve(img);
  30.       });
  31.     },
  32.     {
  33.       threshold: 0.01,
  34.     }
  35.   );
  36.   document.querySelectorAll('img[data-src]').forEach((img) => observer.observe(img));
  37. }
  38. generateItems();
  39. initLazyLoad();
复制代码

技术选型说明
IntersectionObserver 是浏览器原生 API,专门用于异步观察目标元素与根元素的交叉状态。相比 scroll 事件+getBoundingClientRect 的传统做法,IntersectionObserver 具有以下优势:
- 自动优化回调触发时机,减少主线程负担
- 支持阈值(threshold)精确控制加载时机
- 内置 unobserve 方法,加载完成后立即停止观察,避免无效计算
- Chrome 51+、Firefox 55+、Safari 12.1+、Edge 79+ 均已支持

完整代码实现
HTML 结构:只需一个容器 div.card-list,通过 JavaScript 动态生成图片卡片。

CSS 样式:采用 CSS Grid 自适应布局,每张卡片固定高度 497px,图片占满卡片并带悬停缩放效果(1.5 倍)。同时自定义了滚动条样式,提升视觉体验。

核心逻辑解析
1. 图片卡片生成
   使用 DocumentFragment 暂存所有卡片元素,最后一次性 append 到父容器。这避免了循环中逐次操作 DOM 导致的多次重绘重排,在大批量生成(如99张)时性能提升明显。
   初始 img.src 指向一张较小尺寸的占位图,真实图片地址存储在 data-src 自定义属性中,直到图片进入视口才替换。
   动态图片地址通过模板函数生成,每次请求附带不同的随机参数(?r=index),避免浏览器缓存导致的旧图问题。

2. IntersectionObserver 配置
  
  1. threshold: 0.01
复制代码
表示目标元素进入视口 1% 时触发回调。这样设置可以在图片即将完全展现前提前加载,避免用户滚动到位置后出现白屏闪烁。
   入口回调中,先判断 entry.isIntersecting 为 true 才执行加载,否则直接跳过。加载完成后立即调用 observer.unobserve(img),确保同一图片不会重复触发。

3. API 配置项速查
   | 配置项 | 类型 | 默认值 | 说明 |
   |--------|------|--------|------|
   | threshold | number | 0 | 交叉比例阈值,例如 0.5 表示元素50%可见时触发 |
   | root | Element | null | 观察的根容器,默认为浏览器视口 |
   | rootMargin | string | '0px' | 根元素的外边距,可设为 '200px' 实现提前加载 |

性能优化细节
- DocumentFragment 批量插入 DOM
- 加载完成后立即解除观察(unobserve)
- 占位图使用相对较小的图片(例如本示例中的 400x600 缩略图或更小的占位图)
- 可选结合原生 loading="lazy" 属性作为降级方案,但 IntersectionObserver 更精细可控

兼容性及扩展建议
- 如需兼容 IE 或旧版浏览器,可引入 intersection-observer polyfill
- 图片加载失败时,可监听 img.onerror 事件替换为错误占位图,防止裂图
- 实际项目中建议为 data-src 中的图片地址添加 CDN 或其他可靠源
- 若页面上存在大量图片,可考虑将 threshold 设为 0 或使用 rootMargin 提前加载,以优化滚动体验

本方案已在多个生产项目中验证,稳定可靠。开发者只需根据实际需求调整 TOTAL_ITEMS 和图片地址模板即可快速接入。
回复

使用道具 举报

发表于 1 小时前 | 显示全部楼层

Re: JavaScript图片懒加载实战:基于IntersectionObserver API的完整实现

感谢分享,这个实现很完整。我特别欣赏你使用 DocumentFragment 批量操作 DOM 和加载后立即调用 unobserve 的思路,确实能避免不必要的性能开销。关于 threshold 设为 0.01,在实际项目中如果图片在视口边缘快速滚动,是否会因为触发回调轻微延迟而出现短暂空白?我一般会用 rootMargin: '200px' 来提前加载,不知道你怎么看?另外图片加载失败的处理如果能补充一下,比如监听 error 事件替换默认图,就更稳妥了。总之,很实用的方案。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-6-11 17:59 , Processed in 0.033731 second(s), 18 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部