首页图片懒加载实现方案解析

1. IntersectionObserver API(现代推荐方案)

实现原理: 通过观察目标元素与视窗的交叉状态,自动触发回调函数。

代码示例:

javascript 复制代码
const images = document.querySelectorAll('img[data-src]');
const imageObserver = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const img = entry.target;
      img.src = img.dataset.src;
      img.removeAttribute('data-src');
      imageObserver.unobserve(img);
    }
  });
}, { rootMargin: '50px' });

images.forEach(img => imageObserver.observe(img));

优点:

✅ 原生浏览器支持,性能最优

✅ 异步执行,不阻塞主线程

✅ 支持阈值配置和根元素偏移

✅ 代码简洁,易于维护

✅ 支持所有现代浏览器(IE需polyfill)

缺点:

❌ IE不原生支持(需polyfill)

❌ 部分复杂场景配置需要理解

2. 传统滚动监听 + getBoundingClientRect

实现原理: 监听滚动事件,通过getBoundingClientRect()计算元素位置。

代码示例:

javascript 复制代码
function lazyLoadTraditional() {
  const images = document.querySelectorAll('img[data-src]');
  const windowHeight = window.innerHeight;
  
  images.forEach(img => {
    const rect = img.getBoundingClientRect();
    if (rect.top < windowHeight + 100) {
      img.src = img.dataset.src;
      img.removeAttribute('data-src');
    }
  });
}

// 添加防抖优化
const debouncedLazyLoad = debounce(lazyLoadTraditional, 100);
window.addEventListener('scroll', debouncedLazyLoad);
window.addEventListener('resize', debouncedLazyLoad);

优点:

✅ 兼容性好(支持所有浏览器)

✅ 无需额外依赖

✅ 控制精细,可自定义计算逻辑

缺点:

❌ 频繁触发,性能较差

❌ 需要手动优化(防抖/节流)

❌ 代码相对复杂

❌ 计算可能引起重排

3. offsetTop/scrollTop 计算

实现原理: 通过元素相对于文档的偏移位置计算。

代码示例:

javascript 复制代码
function lazyLoadOffset() {
  const images = document.querySelectorAll('img[data-src]');
  const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
  const windowHeight = window.innerHeight;
  
  images.forEach(img => {
    if (img.offsetTop < scrollTop + windowHeight + 200) {
      img.src = img.dataset.src;
      img.removeAttribute('data-src');
    }
  });
}

优点:

✅ 兼容性极好

✅ 计算相对简单

缺点:

❌ 准确性受布局影响

❌ 频繁重排影响性能

❌ 需要处理多种滚动容器

4. 基于 CSS 的 content-visibility

实现原理: 利用CSS新特性延迟渲染不可见内容。

代码示例:

javascript 复制代码
.lazy-container {
  content-visibility: auto;
  contain-intrinsic-size: 0 500px; /* 预估高度 */
}

/* 图片完全加载后移除占位 */
img:not([src]) {
  background: #f0f0f0;
}

优点:

✅ 性能优秀(浏览器原生优化)

✅ 简单易用

✅ 适用于大量列表

缺点:

❌ 兼容性一般(Chrome 85+)

❌ 不是专门为懒加载设计

❌ 需要预估内容尺寸

5. 第三方库方案

常见库:

lozad.js(基于IntersectionObserver) lazysizes(功能全面) vanilla-lazyload(轻量级)

代码示例(lozad.js):

javascript 复制代码
import lozad from 'lozad';

const observer = lozad('.lozad', {
  rootMargin: '100px',
  loaded: (el) => {
    el.classList.add('loaded');
  }
});
observer.observe();

优点:

✅ 功能丰富(支持背景图、iframe等)

✅ 自动降级处理

✅ 社区维护,测试充分

✅ 支持多种加载策略

缺点:

❌ 增加包体积

❌ 学习第三方API

❌ 可能包含不需要的功能

方案详细对比

建议与最佳实践

推荐方案选择

1. 首选方案:IntersectionObserver + 优雅降级

javascript 复制代码
// 示例:带降级的实现
const lazyLoad = (() => {
  if ('IntersectionObserver' in window) {
    // 使用IntersectionObserver
    return (images) => {
      const observer = new IntersectionObserver((entries) => {
        entries.forEach(entry => {
          if (entry.isIntersecting) {
            loadImage(entry.target);
            observer.unobserve(entry.target);
          }
        });
      }, { rootMargin: '50px' });
      
      images.forEach(img => observer.observe(img));
    };
  } else {
    // 降级到传统方案
    return (images) => {
      const lazyLoadTraditional = debounce(() => {
        const windowHeight = window.innerHeight;
        images.forEach(img => {
          const rect = img.getBoundingClientRect();
          if (rect.top < windowHeight + 100) {
            loadImage(img);
          }
        });
      }, 100);
      
      window.addEventListener('scroll', lazyLoadTraditional);
      window.addEventListener('resize', lazyLoadTraditional);
      lazyLoadTraditional(); // 初始执行
    };
  }
})();

2. 关键优化建议:

占位符:使用固定宽高比容器避免布局抖动

渐进加载:先加载低质量占位图(LQIP)

错误处理:添加加载失败的回退方案

预加载:提前加载即将进入视口的图片

3. HTML标记示例:

javascript 复制代码
<div class="image-container" style="aspect-ratio: 16/9;">
  <img 
    data-src="image.jpg" 
    data-srcset="image-small.jpg 300w, image-large.jpg 800w"
    data-sizes="auto"
    alt="描述文本"
    class="lazy-image"
    width="800"
    height="450"
  >
  <!-- 低质量占位 -->
  <div class="lazy-placeholder"></div>
</div>

4. 性能监控:

javascript 复制代码
// 监控图片加载性能
const perfObserver = new PerformanceObserver((list) => {
  list.getEntries().forEach(entry => {
    console.log('Lazy-loaded image timing:', {
      name: entry.name,
      duration: entry.duration,
      startTime: entry.startTime
    });
  });
});
perfObserver.observe({ entryTypes: ['resource'] });

总结

  1. 对于现代应用:IntersectionObserver是首选方案,提供最佳性能和开发体验。

  2. 需要兼容旧浏览器:使用IntersectionObserver polyfill或第三方库(如lazysizes)自动处理降级。

  3. 简单页面/少量图片:传统的滚动监听方案仍然可用,但必须添加防抖/节流优化。

  4. 内容密集型页面:考虑结合CSS content-visibility和懒加载,获得双重性能提升。

  5. 企业级项目:评估使用成熟的第三方库,节省开发时间,获得更多高级功能。

相关推荐
niucloud-admin10 小时前
web 端前端
前端
胖者是谁13 小时前
EasyPlayerPro的使用方法
前端·javascript·css
EndingCoder13 小时前
索引类型和 keyof 操作符
linux·运维·前端·javascript·ubuntu·typescript
liux352813 小时前
Web集群管理实战指南:从架构到运维
运维·前端·架构
沛沛老爹13 小时前
Web转AI架构篇 Agent Skills vs MCP:工具箱与标准接口的本质区别
java·开发语言·前端·人工智能·架构·企业开发
小光学长14 小时前
基于Web的长江游轮公共服务系统j225o57w(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
前端·数据库
Joe55615 小时前
vue2 + antDesign 下拉框限制只能选择2个
服务器·前端·javascript
ChangYan.15 小时前
monorepo 多包管理识别不到新增模块,解决办法
前端·chrome
Jinuss15 小时前
React元素创建介绍
前端·react.js
济61716 小时前
linux 系统移植(第六期)--Uboot移植(5)--bootcmd 和 bootargs 环境变量-- Ubuntu20.04
java·前端·javascript