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

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. 企业级项目:评估使用成熟的第三方库,节省开发时间,获得更多高级功能。

相关推荐
不绝1911 天前
UGUI——进阶篇
前端
Exquisite.1 天前
企业高性能web服务器(4)
运维·服务器·前端·网络·mysql
2501_944525541 天前
Flutter for OpenHarmony 个人理财管理App实战 - 账户详情页面
android·java·开发语言·前端·javascript·flutter
2601_949857431 天前
Flutter for OpenHarmony Web开发助手App实战:快捷键参考
前端·flutter
wangdaoyin20101 天前
若依vue2前后端分离集成flowable
开发语言·前端·javascript
心柠1 天前
vue3相关知识总结
前端·javascript·vue.js
Amumu121381 天前
Vue Router(二)
java·前端
a1117761 天前
图书借阅管理系统(FastAPI + Vue)
前端·vue.js·fastapi
常年游走在bug的边缘1 天前
掌握JavaScript作用域:从函数作用域到块级作用域的演进与实践
开发语言·前端·javascript
极致♀雨1 天前
vue2+elementUI table表格勾选行冻结/置顶
前端·javascript·vue.js·elementui