简介
在对项目进行性能优化时,最常见的优化就是图片的懒加载,懒加载的目的是延迟加载页面上的图片,只有当图片进入视口的时候才加载,这样可以减少初始页面加载时间,节省带宽,提升用户体验。
懒加载有什么好处?
- 减少首屏加载时间:仅加载用户可见区域的图片,降低首次渲染阻塞。
- 节省带宽和流量:避免加载用户可能永远不会看到的内容(如长页面底部的图片)。
- 提升用户体验:防止页面卡顿,尤其是移动端和弱网环境。
- 优化SEO和性能评分:直接影响 Lighthouse 等工具的性能评分指标(如 LCP、FCP)。
在vue项目中(使用 vue-lazyload/vue3-lazyload 库)
- 
安装库: bashnpm install vue-lazyload --save
- 
全局配置 (在 main.js中):javascriptimport Vue from 'vue'; import VueLazyload from 'vue-lazyload'; Vue.use(VueLazyload, { preLoad: 1.3, // 预加载比例 loading: 'path/to/loading.gif', // 占位图 error: 'path/to/error.png', // 加载失败图 attempt: 3 // 重试次数 });
- 
在组件中使用: html<template> <img v-lazy="imageUrl" alt="description"> </template>
在React 项目中(使用 react-lazyload 库)
- 
安装库: bashnpm install react-lazyload --save
- 
包裹组件: jsximport LazyLoad from 'react-lazyload'; function MyComponent() { return ( <LazyLoad height={200} offset={100} placeholder={<LoadingSpinner />}> <img src="image.jpg" alt="description" /> </LazyLoad> ); }
原生JavaScript的实现方法
原生js的实现方法有两种:使用Intersection Observer API和通过监听滚动事件(传统方案)。其中,Intersection Observer更现代,性能更好,而滚动事件监听则是传统方法,兼容性可能更好,但性能较差。
方案1:使用 Intersection Observer API
实现步骤:
- 将图片的真实URL存放在data-src属性中
- 当图片进入可视区域时,将data-src赋值给src属性
- 使用IntersectionObserver监听元素可见性变化
            
            
              html
              
              
            
          
          <!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>图片懒加载示例</title>
  <style>
    .lazy-img {
      width: 100%;
      height: 600px; /* 根据实际需求设置高度 */
      background: #f0f0f0;
      margin: 20px 0;
      object-fit: cover;
    }
  </style>
</head>
<body>
  <!-- 图片容器:真实图片地址存储在data-src -->
  <img class="lazy-img" data-src="https://picsum.photos/800/600?image=1" alt="图片1">
  <img class="lazy-img" data-src="https://picsum.photos/800/600?image=2" alt="图片2">
  <!-- 更多图片... -->
  <script>
    document.addEventListener('DOMContentLoaded', () => {
      // 获取所有需要懒加载的图片元素
      const lazyImages = document.querySelectorAll('.lazy-img');
      
      // 配置Intersection Observer
      const observerOptions = {
        root: null, // 使用视口作为观察区域
        rootMargin: '0px 0px 200px 0px', // 提前200px触发加载
        threshold: 0.1 // 当元素10%进入视口时触发
      };
      // 创建观察器实例
      const observer = new IntersectionObserver((entries, observer) => {
        entries.forEach(entry => {
          if (entry.isIntersecting) { // 当元素进入视口
            const img = entry.target;
            // 替换src属性触发图片加载
            img.src = img.dataset.src;
            // 加载完成后移除观察
            img.addEventListener('load', () => {
              img.classList.add('loaded');
              observer.unobserve(img);
            });
          }
        });
      }, observerOptions);
      // 开始观察所有图片
      lazyImages.forEach(img => observer.observe(img));
    });
  </script>
</body>
</html>代码解析:
- 
HTML结构 - 使用data-src存储真实图片地址
- 初始src为空或使用占位图
- 通过CSS设置图片容器的占位样式
 
- 使用
- 
IntersectionObserver配置 - rootMargin: '0px 0px 200px 0px':提前200px触发加载
- threshold: 0.1:当元素10%进入视口时触发
 
- 
加载流程 - 检测到元素进入视口时替换src属性
- 图片加载完成后移除观察器
 
- 检测到元素进入视口时替换
方案2:传统滚动监听方案(兼容旧浏览器)
            
            
              html
              
              
            
          
          <script>
  // 节流函数:避免频繁触发滚动事件
  function throttle(func, delay = 200) {
    let lastCall = 0;
    return (...args) => {
      const now = new Date().getTime();
      if (now - lastCall < delay) return;
      lastCall = now;
      func.apply(this, args);
    }
  }
  // 判断元素是否进入视口
  function isInViewport(element) {
    const rect = element.getBoundingClientRect();
    return (
      rect.top <= window.innerHeight * 1.5 && // 提前1.5屏加载
      rect.bottom >= 0
    );
  }
  // 加载可见图片
  function lazyLoad() {
    const images = document.querySelectorAll('.lazy-img[data-src]');
    images.forEach(img => {
      if (isInViewport(img)) {
        img.src = img.dataset.src;
        img.removeAttribute('data-src');
      }
    });
  }
  // 初始化
  document.addEventListener('DOMContentLoaded', lazyLoad);
  window.addEventListener('scroll', throttle(lazyLoad));
  window.addEventListener('resize', throttle(lazyLoad));
</script>实践建议
- 
占位符策略 - 使用与图片比例一致的占位容器
- 可添加低质量图片预览(LQIP)
 
- 
性能优化 - 优先使用WebP格式图片
- 配合loading="lazy"原生属性(兼容性需注意)
 html<img src="image.jpg" loading="lazy" alt="...">
实现效果对比
| 指标 | IntersectionObserver | 滚动监听 | 
|---|---|---|
| 性能消耗 | 低(浏览器原生优化) | 高(频繁触发) | 
| 代码复杂度 | 简单 | 复杂 | 
| 浏览器兼容性 | IE不支持(需polyfill) | 全兼容 | 
| 精确度 | 高 | 依赖计算逻辑 | 
注意事项
- 首屏图片不要使用懒加载
- 对SEO关键内容谨慎使用
- 移动端注意rootMargin的合理设置
- 优先使用原生loading="lazy"(需考虑目标用户浏览器)