🚀 别再让浏览器"负重跑"了!手把手教你用 IntersectionObserver 实现图片懒加载
💡 前言:为什么你的网页打开像"PPT"?
想象一下,你正在约会,对面坐着一位美女(用户)。她满怀期待地问你:"嘿,你的网站快吗?"
然后你深吸一口气,开始从背包里往外掏东西:先掏出一张巨大的海报(首屏大图),然后是几百张高清无码的猫咪照片(长列表图片),最后还有几个G的视频......
美女(用户)看着你这一堆乱七八糟的东西,还没等你掏完,她就说了一句:"算了,我们不合适。" 然后转身走了(用户流失,跳出率 100%)。
这就是**不做懒加载(Lazy Load)**的下场。
在传统的网页开发中,我们习惯把所有 <img> 标签的 src 一股脑全写上。浏览器一看:"好家伙,老板发话了,不管三七二十一,全部下载!" 于是,即使用户根本还没滑到页面底部,浏览器就已经累得气喘吁吁,流量在燃烧,内存再尖叫。
今天,我们就来聊聊如何用现代浏览器的"外挂"------ IntersectionObserver,来拯救你那卡顿的网页。
🧐 什么是懒加载?
简单来说,就是**"按需加载"**。
- 用户看哪里,就加载哪里。
- 用户还没滑到的图片?先给个"占位符"(比如一张很轻的loading图或者灰色背景)糊弄一下。
- 等用户快滑到了,再瞬间把真图换上。
这就好比你去自助餐厅,不会一次性把所有菜都堆在桌子上,而是吃一盘,拿一盘。
🛠️ 实战:手写一个"丝滑"的懒加载
以前,我们要实现懒加载,得监听 window.onscroll 事件,然后疯狂计算 getBoundingClientRect,还要防抖节流......写起来简直头秃,性能还差。
现在,IntersectionObserver 来了!它是浏览器原生的 API,专门用来监听元素是否进入了视口(可视区域)。它运行在单独的线程中,性能极佳,简直是前端界的"德芙",纵享丝滑。
来看代码(结合你提供的示例):
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>图片的懒加载</title>
<style>
* { margin: 0; padding: 0; }
/* 搞几个大盒子把图片挤下去,模拟长页面 */
.box { height: 200vh; background-color: #eee; }
img { width: 100%; height: 400px; object-fit: cover; display: block; }
</style>
</head>
<body>
<!-- 占位用的大盒子 -->
<div class="box"></div>
<!--
1. src: 先放一张极小的默认图(或者loading图),防止页面布局塌陷
2. data-src: 把真正的高清大图藏在这里,浏览器不会主动加载它
-->
<img class="lazy"
src="https://img10.360buyimg.com/wq/jfs/t24601/190/890984006/4559/731564fc/5b7f9b7bN3ccd29ab.png"
data-src="https://img.36krcdn.com/hsossms/20260119/v2_53cad3f2226f48e2afc1942de3ab74e4@5888275@ai_oswg1141728oswg1053oswg495_img_png~tplv-1marlgjv7f-ai-v3:960:400:960:400:q70.jpg?x-oss-process=image/format,webp"
alt="AI生图1">
<div class="box"></div>
<img class="lazy"
src="https://img10.360buyimg.com/wq/jfs/t24601/190/890984006/4559/731564fc/5b7f9b7bN3ccd29ab.png"
data-src="https://img.36krcdn.com/hsossms/20260117/v2_1e74add07bb94971845c777e0ce87a49@000000@ai_oswg421938oswg1536oswg722_img_000~tplv-1marlgjv7f-ai-v3:960:400:960:400:q70.jpg?x-oss-process=image/format,webp"
alt="AI生图2">
<script>
// 1. 选中所有需要懒加载的图片
const images = document.querySelectorAll('.lazy');
// 2. 创建观察者实例
// 浏览器提供的观察者模式,自动观察,性能杠杠的
const observer = new IntersectionObserver((entries) => {
// entries 是一个数组,包含所有被观察元素的状态
entries.forEach(entry => {
// isIntersecting 为 true 表示元素进入了视口
if (entry.isIntersecting) {
const img = entry.target; // 获取目标元素 (DOM节点)
const original_img = img.dataset.src; // 获取 data-src 里的真图地址
console.log('抓到你了!正在加载:', original_img);
// 3. 偷梁换柱:把真图地址赋值给 src
img.src = original_img;
// 4. 加载完后,告诉观察者:"这人看过了,不用盯着了"
// 停止观察该元素,节省性能
observer.unobserve(img);
}
})
})
// 5. 开始观察!
images.forEach(img => observer.observe(img));
</script>
</body>
</html>
🔍 代码核心知识点解析
别光顾着复制粘贴,我们来拆解一下这里的"骚操作":
-
data-src的妙用 🎭 HTML 标准规定,<img>标签只要有src属性,浏览器就会立刻发起请求。为了阻止这个行为,我们把真实的图片链接藏在自定义属性data-src里。浏览器:"哦,这只是一个叫 data-src 的字符串,我不认识,不加载。" -
IntersectionObserverAPI 👁️ 这是主角。- 传统做法:你得不停地问浏览器"图片在哪?滚动条在哪?算一下距离......"(强制重排/重绘,累死CPU)。
- IntersectionObserver:你告诉浏览器"帮我盯着这张图,它出来了叫我一声"。浏览器在底层异步处理这些计算,完全不影响页面渲染帧率。
-
entry.isIntersecting✅ 这是一个布尔值。true代表元素和视口有交集(出现了),false代表没交集(消失了)。我们在true的时候才去加载图片。 -
observer.unobserve(img)🛑 这点很重要!图片加载完了,任务就结束了。如果不取消观察,浏览器还会一直盯着这张已经加载好的图片,纯属浪费资源。用完即弃,才是好代码。
🤔 为什么要这么做?(必要性)
你可能会问:"我就几张图,直接加载不行吗?"
- 首屏速度(First Contentful Paint):用户打开网页,只关心第一屏。如果后台在偷偷下载第10屏的图片,首屏加载就会变慢。懒加载能让首屏飞起来。
- 节省带宽:很多用户可能根本不会滑到底部。你加载了100张图,他只看了10张。懒加载帮他省了90%的流量,他会感谢你的(特别是用5G流量看视频的时候)。
- 减少内存占用:浏览器同时处理几百个网络请求和渲染几百张大图,内存容易爆炸,导致页面卡顿甚至崩溃。
📌 总结
懒加载是现代 Web 开发的标配。
以前我们用 jQuery 写插件,后来用原生 JS 算坐标,现在我们有了 IntersectionObserver。技术总是在进步,我们要学会用更优雅、性能更好的方式去解决问题。
下次再有人问你网站为什么这么快,你可以淡淡地喝一口咖啡,说:"哦,我只是让我的图片学会了'按需出现'而已。"
Happy Coding! ☕️
(本文代码已在 Chrome/Firefox/Edge 等现代浏览器测试通过。IE 用户?请出门右转不送,或者加个 Polyfill 吧。)