老板,你家网站打开慢?别急着怪服务器——八成是懒加载写歪了。

我上周刚帮一个做母婴电商的朋友调页面,首页3秒才出图,结果发现他贴的那段“懒加载代码”,其实压根没在动。不是懒加载没用,是它根本没被正确唤醒。

为什么你的懒加载没起作用?先检查这3个毛病

第一个问题:监听滚动,但没管手机怎么“滑”。

很多代码抄的是 PC 端那一套,靠 scroll 事件 + 节流。可手机上用户一划就是一屏,滚动事件触发稀疏又滞后,图片经常已经滑过去了,才开始加载。你拿手机试一下:快速往下划,是不是有几张图“啪”一下才蹦出来?那就是监听没跟上节奏。

第二个问题:只懒了图,却放任视频和 iframe 在后台狂奔。

有个做知识付费的客户,课程列表页塞了12个视频封面,懒加载只绑了 <img>,结果每个 <iframe> 一进 DOM 就自动加载 YouTube 的整套 JS,页面直接卡住。用户还没点开,浏览器已经在替他下载半个视频站了。

第三个问题:用了 loading="lazy",但忘了 Safari 还在“观望”。

这个属性确实省事,Chrome、Firefox 里跑得挺欢,可 Safari 直到 iOS 16.4 才开始稳定支持 iframe 的懒加载,图片支持更晚。你测的时候用 Chrome 觉得丝滑,用户用 iPhone 打开,所有图全挤在首屏加载——白优化。

图片懒加载的正确姿势:Intersection Observer 才是王道

别再手写 scroll + getBoundingClientRect() 了。算得勤,卡得也勤;算得松,图又提前加载,懒了个寂寞。

Intersection Observer 是浏览器自己盯视口,不抢主线程,也不用你操心节流防抖。它能精准告诉你:“这张图,还有 150px 就要露头了”。

写法就三步:

  1. 创建观察器,配好 rootMargin: "150px"(提前加载距离)
  2. 给每张图加个 data-src 存真实地址,src 先指向一张 1×1 的透明占位图
  3. 回调里判断 entry.isIntersecting,为真就 img.src = img.dataset.src,再 observer.unobserve(img)

我们给一个卖户外装备的网站换掉老 jQuery 插件后,列表页首屏请求数从 28 个压到 4 个,滑动时图是“跟着手指出来”的,没断层也没延迟。IE11 需要补个 polyfill,其他主流浏览器全原生支持。

视频和iframe懒加载:别让用户为你的偷懒买单

YouTube 嵌入代码默认带 400KB+ 的 JS 和样式,一个 iframe 就够呛;三个?用户还没点播放键,你页面已经下完一首歌的体积了。

视频正确做法:用 poster 属性挂张高清封面图,<video> 标签里 preload="none",等用户点一下再 load() 或自动播放。

iframe 更得收着点:把 src 先删掉,换成 data-src,DOM 里只留个空壳或一张静态背景图。等它快进视口了,再用 JS 创建新 iframe,把 data-src 赋给 src,插进原来位置。

之前帮一个旅游攻略站改地图嵌入——原来 5 个 <iframe src="https://maps.google.com/..."> 一上来全加载,连带地图 API、定位逻辑、街景资源一股脑下载。改成懒加载后,用户点开某个景点才加载对应地图,页面首次渲染快了一大截,编辑反馈说“终于敢多放地图了”。

懒加载对SEO的影响:Google怎么看?别被谣言骗了

有人说“Google 爬虫看不见懒加载的图,收录会掉”。这话前半句对,后半句错——前提是,你得让 URL 在源码里“露个脸”。

Google 自 2019 年起就能解析 data-src,只要你别把图片地址藏在 JS 变量里、或者靠 AJAX 异步吐出来。关键就一条:真实地址得在 HTML 字符串里,明明白白写着。

所以标准写法是:

<img 
  src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" 
  data-src="https://example.com/product-1.jpg" 
  alt="登山包特写,防水耐磨"
>

alt 别空着,title 也加上(虽然权重低,但有比没有强)。我们帮一个美食博客上线懒加载后,连续跟踪两个月:收录量稳住,长尾词排名没掉,跳出率反而降了——因为图出来快了,用户愿意多看两屏。

实战中的3个坑:我替你踩过了,别再踩

坑一:占位图尺寸没锁死,页面“抽搐”

用 Base64 占位图很省事,但如果没给容器设宽高,或者真实图比例和占位图不一致,图一加载,下面内容“唰”地往下掉一块。用户正读文字,突然跳行,体验直接打五折。

解法很简单:要么在 <img> 上写死 widthheight,要么用 CSS aspect-ratio: 4/3 锁定宽高比,再配合 object-fit: cover

坑二:CDN 缓存和懒加载“打架”

有些 CDN 对带查询参数的图片 URL 缓存不友好(比如 ?width=400),而懒加载常靠动态拼参生成图地址。结果每次请求都穿透回源,比不懒还慢。

建议:检查你 CDN 后台的缓存规则,确保 srcset 里的尺寸参数、data-src 中的缩略图路径,都被识别为可缓存资源。如果 CDN 支持,直接开它的图片懒加载开关,比自己写更稳。

坑三:返回列表页,图又“重来一遍”

用户从商品列表点进详情页,再按返回键,列表页那些刚加载过的图,可能被浏览器当“非活跃资源”清掉了,一回来又重新懒加载——闪一下,再出图。

解法有两个:

  • 在列表页 <head> 里加 <link rel="prefetch" href="xxx.jpg">,预取用户大概率要看的图
  • 或者更轻量:监听 pageshow 事件,如果是从缓存恢复(event.persisted === true),手动触发一次 img.src = img.dataset.src

今天就能做的3个优化动作

第一,打开 Chrome,按 F12 → 切到 Network 面板 → 刷新页面 → 在过滤框输 img,看首屏(Filtered by “img”)加载了几个图片请求。如果超过 6 个,说明懒加载大概率没生效。马上检查:首屏 <img>loading 属性是不是误设成了 lazy?该改成 eager

第二,如果你用 WordPress,进「外观 → 主题编辑器」或「插件 → 已安装插件」,搜一搜有没有叫 “Lazy Load”、“Image Optimization” 的插件。很多主题自带的懒加载是 scroll + jQuery 老方案,容易卡顿。今天就关掉它,在 functions.php 里加几行 IntersectionObserver 代码,或者装个轻量插件如 WP Rocket(它内置现代懒加载,且默认兼容 Safari)。

第三,顺手给所有 <img> 补上 srcsetsizes。不用重做图,用你现有图传两套尺寸就行:比如原图 product.jpg,再传个 product-600w.jpg,然后写:

<img 
  src="product-600w.jpg" 
  srcset="product-600w.jpg 600w, product.jpg 1200w" 
  sizes="(max-width: 768px) 600px, 1200px"
  data-src="product.jpg"
  loading="lazy"
>

这样懒加载 + 响应式一步到位,手机用户不下载大图,流量省了,加载快了。

现在就打开你网站的首页,按 F12,看一眼 Network 里的 img 请求——30 秒,够你确认问题在不在那儿。