JavaScript 实现图片懒加载

文档概述

本文基于原生 JavaScript IntersectionObserver API 实现的图片懒加载方案知识库说明,通过监听元素与视口的交叉状态,仅在图片进入视口时加载真实图片,减少初始页面加载资源消耗,提升页面性能与用户体验。

核心特性

  1. 原生 API 实现 :基于 IntersectionObserver,无需引入第三方库,兼容性良好
  2. 性能优化 :使用 DocumentFragment 批量操作 DOM,减少重绘重排
  3. 占位图策略:初始加载默认占位图,进入视口后替换为真实图片
  4. 自动停止观察:图片加载完成后自动取消观察,避免重复触发
  5. 视觉交互:包含卡片悬停缩放效果、自定义滚动条样式,提升用户体验

依赖说明

  • 核心技术:原生 JavaScript(ES6+)、HTML5、CSS3

  • 浏览器兼容性:支持 IntersectionObserver API 的现代浏览器(Chrome 51+、Firefox 55+、Safari 12.1+、Edge 79+)

  • 外部资源:

    • 默认占位图:https://pica.zhimg.com/v2-f052aa50ca65df4bad1c3b7e4084d00e_1440w.jpg
    • 动态图片模板:https://picsum.photos/400/600?r={index}

完整代码实现

1. HTML 结构

xml 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />
  <title>JavaScript Observer 实现懒加载</title>
  <link rel="stylesheet" href="./css/index.css" />
</head>
<body>
  <div class="card-list"></div>
  <script src="./js/index.js"></script>
</body>
</html>

2. CSS 样式

css 复制代码
* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  background-color: #f5f6f7;
}

.card-list {
  --ap-gap: 16px;
  --ap-min-width: 300px;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(var(--ap-min-width), 1fr));
  gap: var(--ap-gap);
  padding: 16px;
}

.card-list .item {
  cursor: pointer;
  height: 497px;
  border-radius: 10px;
  box-shadow: 0 0 6px #000;
  overflow: hidden;
}

.card-list .item:hover img {
  transform: scale(1.5);
}

.card-list .item img {
  display: block;
  width: 100%;
  height: 100%;
  transition: all 0.32s;
}

/* 自定义滚动条样式 */
::-webkit-scrollbar {
  width: 6px;
  height: 6px;
}
::-webkit-scrollbar-track {
  background-color: #f5f5f5;
}
::-webkit-scrollbar-track-piece {
  border-radius: 6px;
  background-color: #f5f5f5;
}
::-webkit-scrollbar-thumb {
  border-radius: 6px;
  background-color: #ccc;
}
::-webkit-scrollbar-thumb:hover {
  background-color: #a8a8a8;
}
::-webkit-scrollbar-thumb:active {
  background-color: #787878;
}
::-webkit-scrollbar-corner {
  background-color: #f5f5f5;
}
::-webkit-resizer {
  background-repeat: no-repeat;
  background-position: bottom right;
}

3. JavaScript 逻辑

ini 复制代码
// 配置项
const TOTAL_ITEMS = 99; // 总图片数量
const DEFAULT_IMG = 'https://pica.zhimg.com/v2-f052aa50ca65df4bad1c3b7e4084d00e_1440w.jpg'; // 默认占位图
const IMG_URL_TEMPLATE = (index) => `https://picsum.photos/400/600?r=${index}`; // 动态图片模板

const cardList = document.querySelector('.card-list');

/**
 * 生成图片卡片
 * 使用 DocumentFragment 批量操作 DOM,减少重绘重排
 */
function generateItems() {
  const fragment = document.createDocumentFragment();
  for (let i = 0; i < TOTAL_ITEMS; i++) {
    const div = document.createElement('div');
    div.classList.add('item');
    
    const img = document.createElement('img');
    img.src = DEFAULT_IMG; // 初始设置占位图
    img.dataset.src = IMG_URL_TEMPLATE(i); // 真实图片地址存储在 data-src
    img.alt = `Image ${i + 1}`;
    
    div.appendChild(img);
    fragment.appendChild(div);
  }
  cardList.appendChild(fragment); // 一次性插入所有元素
}

/**
 * 初始化 Intersection Observer
 * 监听图片与视口的交叉状态,实现懒加载
 */
function initLazyLoad() {
  const observer = new IntersectionObserver(
    (entries, observer) => {
      entries.forEach((entry) => {
        if (!entry.isIntersecting) return; // 跳过未进入视口的元素
        
        const img = entry.target;
        img.src = img.dataset.src; // 替换为真实图片
        observer.unobserve(img); // 加载完成后停止观察
      });
    },
    {
      threshold: 0.01, // 交叉阈值:元素 1% 进入视口时触发
    }
  );

  // 观察所有带有 data-src 属性的图片
  document.querySelectorAll('img[data-src]').forEach((img) => observer.observe(img));
}

// 执行主逻辑
generateItems();
initLazyLoad();

核心实现说明

1. 图片卡片生成

  • DocumentFragment 优化:使用文档片段暂存所有卡片元素,最后一次性插入 DOM,避免多次操作 DOM 导致的性能损耗
  • 占位图策略 :初始 img.src 设置为默认占位图,真实图片地址存储在 data-src 自定义属性中
  • 动态图片地址 :通过模板函数 IMG_URL_TEMPLATE 生成唯一的动态图片地址,避免缓存

2. Intersection Observer 初始化

  • 交叉阈值(threshold) :设置为 0.01,即图片 1% 进入视口时触发加载,提前加载提升流畅度
  • 回调处理 :遍历所有观察项,仅处理进入视口的元素(entry.isIntersectingtrue
  • 自动停止观察 :图片加载完成后调用 observer.unobserve(img),避免重复触发回调,节省性能

API 说明

IntersectionObserver 配置

配置项 类型 默认值 说明
threshold number 0 元素与视口的交叉比例阈值,达到该比例时触发回调
root Element null 观察的根元素,默认为视口
rootMargin string '0px' 根元素的外边距,用于提前或延迟触发

核心方法

方法名 说明
observe(target) 开始观察目标元素
unobserve(target) 停止观察目标元素
disconnect() 停止观察所有元素

使用示例

直接在浏览器中打开 HTML 文件即可体验:

  1. 页面初始加载 99 个带占位图的卡片
  2. 滚动页面,卡片进入视口时自动加载真实图片
  3. 鼠标悬停卡片,图片放大 1.5 倍
  4. 滚动条样式自定义,提升视觉体验

注意事项

  1. 浏览器兼容性IntersectionObserver 在 IE 及旧版本浏览器中不支持,如需兼容可引入 polyfill(如 intersection-observer
  2. 占位图优化:建议使用尺寸较小的占位图,减少初始加载资源
  3. 图片地址有效性 :确保 data-src 存储的真实图片地址可访问
  4. 性能扩展 :可结合 loading="lazy" 属性(原生懒加载)作为降级方案
  5. 错误处理:可添加图片加载失败的回调,替换为错误占位图
  6. 阈值调整 :可根据实际需求调整 threshold 值,平衡加载时机与性能
相关推荐
kyriewen2 小时前
事件流与事件委托:当点击按钮时,浏览器里发生了什么?
前端·javascript·面试
wang09072 小时前
Linux性能优化之上下文切换
linux·运维·性能优化
Alanzeeb2 小时前
博客系统测试文档
java·javascript·功能测试·可用性测试
chenhdowue2 小时前
Vue 表格组件 vxe-table 进阶,灵活导出指定数据的 CSV 文件
javascript·vue.js·vxe-table
UTwelve2 小时前
【UE】如何正确旋转法线贴图
性能优化·ue5·材质·贴图·着色器
清汤饺子2 小时前
Everything Claude Code:让我把 AI 编程效率再翻一倍的东西
前端·javascript·后端
西洼工作室2 小时前
React TabBar切换与高亮实现
前端·javascript·react.js
wuhen_n2 小时前
Tool Schema 设计模式详解
前端·javascript·ai编程
wuhen_n2 小时前
排列算法完全指南 - 从全排列到N皇后,一套模板搞定所有排列问题
前端·javascript·算法