loading="lazy"仅对静态HTML中书写的img和iframe生效,对广告位、首屏关键图、动态插入或跨域iframe无效;必须用data-src+IntersectionObserver方案,并预留尺寸、设sandbox、手动触发SDK刷新。

直接给结论:能用 loading="lazy" 就别手写 JS,但首屏必须显示、广告位、跨域动态插入的 iframe,它基本没用——得切到 data-src + IntersectionObserver 才真正可控。
为什么 loading="lazy" 有时根本不起作用
这个属性只对 HTML 中静态书写的 <iframe></iframe> 生效,且依赖浏览器原生支持(Chrome 77+、Firefox 75+、Safari 15.4+)。常见失效场景包括:
- 广告 SDK 动态插入的 iframe —— 浏览器压根不解析它的
loading属性 - src 带时间戳或随机参数(如
?t=1712345678)—— 缓存失效,请求照发,loading变成摆设 - 页面在微信 iOS 内置浏览器或旧版安卓 WebView 中打开 —— 属性被忽略,回退为 eager 加载
- 同时写了
src和loading="lazy"—— 浏览器可能立刻发起请求,Observer 还没注册就加载完了
data-src + IntersectionObserver 怎么写才不出错
这是目前最通用、可预测、能配合业务逻辑(比如广告计费、用户滚动行为)的方案。关键不是“用了 Observer”,而是怎么用:
- 初始 HTML 中完全不写
src,只放 - Observer 的
threshold别设成0,用0.2(20% 可见即触发),避免用户快速滑过却没加载 - 触发后要立即调用
observer.unobserve(iframe),否则滚动来回会重复加载 - iframe 加载完成前,务必加 skeleton 占位或 loading 提示,否则白屏明显
- 如果 iframe 可能复用(如 tab 切换),加载后标记
data-loaded="true",下次直接iframe.style.display = "block"
广告 iframe 必须绕开 loading="lazy"
广告位延迟加载不是“可选项”,是性能底线。但广告 iframe 有特殊约束:
立即学习“前端免费学习笔记(深入)”;
- 禁用
loading="lazy"—— 它对 GAM、广点通等返回的跨域 iframe 无效,Network 面板里仍显示Priority: high - 容器必须用
<div class="ad-slot" data-ad-unit="/123/home_top"></div>,不写任何 iframe 标签 - 创建 iframe 时必须设
sandbox="allow-scripts allow-popups",防止广告脚本污染主域 - src 注入后,必须手动调用广告 SDK 的
refresh()或display()方法,否则不计费也不展示 - 预留固定宽高(如
height: 250px),否则触发 CLS(布局偏移),影响 Core Web Vitals
首屏 iframe 卡顿,别只盯着加载时机
即使 src 已设好、也进了视口,首屏 iframe 第一次打开依然卡,往往不是加载慢,而是内部页面太重:
- 检查嵌入页是否触发了 301/302 跳转 —— 每次跳转多一个 RTT,弱网下就是 600ms+
- 确认响应头含
Cache-Control: public, max-age=3600,而不是no-cache或带随机 query 参数 - 嵌入页 JS 是否有长任务?同步
alert()、未分片的大循环、全量 Vue/React 包 —— 这些会冻结主页面交互 - 同源下可把公共依赖(如 lodash、moment)提到主页面,嵌入页通过
window.parent访问,省掉重复下载 - 跨域嵌入页无法共享 DNS/TLS 连接,若域名不同,提前在
加<link rel="preconnect" href="https://widget.cdn.net">
真正难的不是“怎么让它晚点加载”,而是“怎么让它加载完不拖垮主线程”——尤其是嵌入页不可控时,sandbox、postMessage 隔离、尺寸预留这些细节,漏一项就可能让优化白做。



















