文档概述
本文基于原生 JavaScript IntersectionObserver API 实现的图片懒加载方案知识库说明,通过监听元素与视口的交叉状态,仅在图片进入视口时加载真实图片,减少初始页面加载资源消耗,提升页面性能与用户体验。
核心特性
- 原生 API 实现:基于
IntersectionObserver,无需引入第三方库,兼容性良好 - 性能优化:使用
DocumentFragment批量操作 DOM,减少重绘重排 - 占位图策略:初始加载默认占位图,进入视口后替换为真实图片
- 自动停止观察:图片加载完成后自动取消观察,避免重复触发
- 视觉交互:包含卡片悬停缩放效果、自定义滚动条样式,提升用户体验
依赖说明
-
核心技术:原生 JavaScript(ES6+)、HTML5、CSS3
-
浏览器兼容性:支持
IntersectionObserverAPI 的现代浏览器(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 结构
<!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 样式
* {
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 逻辑
// 配置项
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.isIntersecting为true) - 自动停止观察:图片加载完成后调用
observer.unobserve(img),避免重复触发回调,节省性能
API 说明
IntersectionObserver 配置
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
threshold | number | 0 | 元素与视口的交叉比例阈值,达到该比例时触发回调 |
root | Element | null | 观察的根元素,默认为视口 |
rootMargin | string | '0px' | 根元素的外边距,用于提前或延迟触发 |
核心方法
| 方法名 | 说明 |
|---|---|
observe(target) | 开始观察目标元素 |
unobserve(target) | 停止观察目标元素 |
disconnect() | 停止观察所有元素 |
使用示例
直接在浏览器中打开 HTML 文件即可体验:
- 页面初始加载 99 个带占位图的卡片
- 滚动页面,卡片进入视口时自动加载真实图片
- 鼠标悬停卡片,图片放大 1.5 倍
- 滚动条样式自定义,提升视觉体验
注意事项
- 浏览器兼容性:
IntersectionObserver在 IE 及旧版本浏览器中不支持,如需兼容可引入 polyfill(如intersection-observer) - 占位图优化:建议使用尺寸较小的占位图,减少初始加载资源
- 图片地址有效性:确保
data-src存储的真实图片地址可访问 - 性能扩展:可结合
loading="lazy"属性(原生懒加载)作为降级方案 - 错误处理:可添加图片加载失败的回调,替换为错误占位图
- 阈值调整:可根据实际需求调整
threshold值,平衡加载时机与性能