标签本身不支持loading属性,仅内部标签支持;loading="lazy"必须写在上,上无效,且需确保有src或srcset、宽高及现代浏览器支持。

picture 标签本身不支持 loading 属性
<picture></picture> 是一个容器标签,它不直接渲染图像,也不接受 loading 属性。浏览器会忽略写在 <picture></picture> 上的 loading="lazy"。真正触发懒加载的是内部的 <img> 元素——只有它才原生支持 loading 属性(Chrome 76+、Firefox 75+、Safari 15.4+)。
所以正确做法是:把 loading="lazy" 放到 <picture></picture> 内部的 <img> 标签上,而不是 <picture></picture> 或 <source></source> 上。
正确的 lazy-loading picture 结构示例
以下结构能正常触发懒加载,且兼容响应式场景:
<picture><source media="(min-width: 1024px)" srcset="hero-large.webp" type="image/webp"><source media="(min-width: 768px)" srcset="hero-medium.jpg"> @@##@@ </source></source></picture>
注意几点:
-
<source></source>标签不能加loading,加了也无效 -
<img src="hero-small.jpg" alt="Hero" loading="lazy">的src是 fallback 必填项,也是懒加载的实际载体 -
loading="lazy"在<img>上生效时,会基于其在视口中的位置和src(或srcset)触发加载,与<source></source>的匹配逻辑无关 - 如果
<img>同时有srcset和sizes,loading="lazy"依然有效,浏览器会在懒加载触发后按需选择最合适的资源
常见失效原因和坑
即使写了 loading="lazy",也可能没效果,典型原因包括:
- 目标
<img>距离视口顶部太近(默认阈值约 1250px),浏览器认为“马上要进入”,提前加载了 - 页面启用了
document.createElement('img').loading = 'lazy'类似的 JS 动态创建逻辑,但未设置src或srcset,导致懒加载被跳过 - 使用了
loading="eager"覆盖,或通过 CSS 设置了position: fixed/transform等影响滚动根节点的样式,干扰了懒加载检测 - Safari 旧版本(loading,需降级用 IntersectionObserver 手动实现
需要兼容老浏览器时怎么办
如果必须支持 Safari loading,得手动控制 <img> 的 src:
先移除 <img> 的 src,用 data-src 存地址,再配合 IntersectionObserver 监听进入视口时赋值:
<picture><source media="(min-width: 1024px)" srcset="hero-large.webp"> @@##@@ </source></picture>
JS 部分只需监听并填充 src:
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
observer.unobserve(img);
}
});
});
document.querySelectorAll('.js-lazy-img').forEach(img => observer.observe(img));
注意:此时 <source></source> 的 srcset 不会被自动读取,所有分辨率适配逻辑要由 JS 或服务端完成,复杂度明显上升。
原生 loading="lazy" 看似简单,但它的行为高度依赖 <img> 的存在和属性完整性;一旦嵌套在 <picture></picture> 里,就容易误以为“整个结构可懒加载”,其实只有一层起作用。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











