如今的网站充斥着图像和视频等大型媒体资源。 图像占平均网站流量的 约 50%。 然而,其中许多图像从未向用户显示,因为它们放置在远离 视窗区域 的位置。
您可能想知道图像延迟加载是怎么回事? 延迟加载 是我们在 CSS-Tricks 上 多次介绍 的内容,包括一份 使用 JavaScript 的不同方法的详细文档指南。 简而言之,我们正在谈论一种机制,该机制会延迟加载内容所需的网络流量,直到需要它为止 - 更准确地说,当内容进入视窗时才触发加载。
好处是什么? 页面初始尺寸更小,加载速度更快,并为可能不需要的项目节省了网络请求(如果用户从未到达该项目)。
如果您阅读了其他关于延迟加载的指南(在本网站或其他网站上),您会发现我们不得不采取不同的策略来使延迟加载工作。 好吧,这种情况即将改变,因为延迟加载将在 HTML 中以新的 loading
属性的形式提供原生支持… 至少在 Chrome 中,这有望带来更广泛的采用。 Chrome 目前正在开发和测试对原生延迟加载的支持,预计将在 Chrome 76 中启用,该版本计划于 2019 年 7 月 30 日发布。

原生方法之前的方案
到目前为止,像我们这样的开发人员不得不使用 JavaScript(无论是库还是从头开始编写的代码)才能实现延迟加载。 大多数库的工作方式如下
- 初始的服务器端 HTML 响应包含一个没有
src
属性的img
元素,因此浏览器不会加载任何数据。 相反,图像的 URL 被设置为元素数据集中另一个属性的值,例如data-src
。
<img data-src="https://tiny.pictures/example1.jpg" alt="...">
<script src="LazyLoadingLibrary.js"></script>
<script>LazyLoadingLibrary.run()</script>
data-src
属性的值复制到之前为空的 src
属性来实现这一点。<img src="https://tiny.pictures/example1.jpg" data-src="https://tiny.pictures/example1.jpg" alt="...">
这种方法已经行之有效,并能完成任务。 但它并不理想,原因如下。
这种方法的明显问题是网站显示的 关键路径 的长度。 它包含三个步骤,这些步骤必须按顺序执行(一个接一个)
- 加载初始 HTML 响应
- 加载延迟加载库
- 加载图像文件
如果这种技术用于视窗区域上方的图像,网站在加载过程中会闪烁,因为它首先在没有图像的情况下绘制(在步骤 1 或 2 之后,具体取决于脚本是否使用 defer
或 async
),然后在加载图像后包含该图像。 它也会让人感觉加载缓慢。
此外,延迟加载库本身会对网站的带宽和 CPU 要求造成 额外负担。 而且,别忘了 JavaScript 方法不适用于禁用 JavaScript 的用户(尽管我们不应该在 2019 年关心他们,应该吗?)。
哦,还有像 CSS-Tricks 这样依赖 RSS 分发内容的网站怎么办? 初始的无图像渲染意味着 RSS 版本的内容中也没有图像。
等等。
原生延迟加载来拯救!

正如我们在开头提到的,Chromium 和 Google Chrome 将从 Chrome 75 开始以新的 loading
属性的形式提供原生延迟加载机制。 我们将稍后介绍该属性及其值,但让我们首先在浏览器中让它工作,以便我们一起查看。
启用原生延迟加载
在 Chrome 75 及更高版本中,我们可以通过切换两个标志手动启用延迟加载。 预计 Chrome 将从 76 版本开始默认启用此功能(计划于 2019 年 7 月 30 日发布)。
- 在 Chromium 或 Chrome Canary 中打开
chrome://flags
。 - 搜索
lazy
。 - 启用“启用延迟图像加载”和“启用延迟框架加载”标志。
- 使用屏幕右下角的按钮重新启动浏览器。

您可以通过打开 JavaScript 控制台 (F12
) 来检查此功能是否已正确启用。 您应该看到以下警告
“[干预] 延迟加载的图像被替换为占位符。 加载事件被延迟。”
都准备好了吗? 现在我们可以深入研究 loading
属性了。
loading 属性
img
和 iframe
元素都接受 loading
属性。 重要的是要注意,浏览器不会将它的值视为严格的顺序,而是将其视为提示,以帮助浏览器自行决定是否延迟加载图像或框架。
该属性可以具有三个值,如下所述。 在图像旁边,您会发现表格,其中列出了此页面加载的各个资源加载计时。 范围响应 指的是一种部分预先飞行请求,用于确定图像的尺寸(有关详细信息,请参见 工作原理)。 如果此列已填充,则浏览器已成功执行范围请求。
请注意 startTime 列,它表示 DOM 解析后延迟图像加载的时间。 您可能需要执行硬重载 (CTRL + Shift + R) 才能重新触发范围请求。
auto(或 unset)值
<img src="auto-cat.jpg" loading="auto" alt="...">
<img src="auto-cat.jpg" alt="...">
<iframe src="https://css-tricks.cn/" loading="auto"></iframe>
<iframe src="https://css-tricks.cn/"></iframe>

将 loading
属性设置为 auto
(或直接省略值,例如 loading=""
)可以让 浏览器自行决定 是否延迟加载图像。 它会考虑很多因素来做出决定,例如平台、是否启用了数据节省模式、网络状况、图像大小、图像与框架、CSS display
属性等。(有关所有这些因素为何重要的信息,请参见 工作原理)。
eager 值
<img src="auto-cat.jpg" loading="eager" alt="...">
<iframe src="https://css-tricks.cn/" loading="eager"></iframe>

eager
值为浏览器提供了提示,表明应 立即加载 图像。 如果加载已被延迟(例如,因为它已设置为 lazy
,然后由 JavaScript 更改为 eager
),浏览器应该立即开始加载图像。
lazy 值
<img src="auto-cat.jpg" loading="lazy" alt="...">
<iframe src="https://css-tricks.cn/" loading="lazy"></iframe>

lazy
值为浏览器提供了提示,表明应延迟加载图像。 浏览器会自行解释这到底意味着什么,但 解释文档 中指出,它应该在用户滚动到图像 “附近” 时开始加载,这样当图像实际进入视窗时,它可能已经加载完毕。
加载属性的工作原理
与 JavaScript 延迟加载库形成对比的是,原生延迟加载使用一种预先请求的方式来获取 **图像文件的首个 2048 字节**。使用这些字节,浏览器尝试确定图像的尺寸,以便为完整图像插入一个不可见的占位符,并防止内容在加载过程中跳动。
图像的 `load` 事件会在完整图像加载后立即触发,无论是第一次请求(对于小于 2 KB 的图像)还是第二次请求。请注意,对于某些图像,load 事件可能永远不会触发,因为不会进行第二次请求。
将来,浏览器可能比当前提议中的请求次数多一倍。首先是范围请求,然后是完整请求。确保您的服务器支持 HTTP `Range: 0-2047` 标头并以状态码 `206 (Partial Content)` 响应,以防止它们两次传送完整图像。
由于同一用户发出的后续请求数量增加,Web 服务器对 HTTP/2 协议的支持将变得更加重要。
让我们谈谈延迟内容。Chrome 的渲染引擎 Blink 使用启发式方法来确定哪些内容应该被延迟以及延迟多长时间。您可以在 Scott Little 的设计文档 中找到对要求的全面列表。以下是对将被延迟的内容的简短概述。
- 所有平台上设置了 `loading="lazy"` 的图像和框架。
- Chrome for Android 上启用了 Data Saver 且满足以下所有条件的图像。
loading="auto"
或未设置。- 没有 `width` 和 `height` 属性,小于 10 像素。
- 不是在 JavaScript 中以编程方式创建的。
- 满足以下所有条件的框架。
loading="auto"
或未设置。- 来自第三方(与嵌入页面的域名或协议不同)。
- 高度和宽度大于 4 像素(以防止延迟微小的跟踪框架)。
- 未标记为 `display: none` 或 `visibility: hidden`(同样,以防止延迟跟踪框架)。
- 没有使用负 `x` 或 `y` 坐标定位在屏幕外。
具有 srcset 的响应式图像
原生延迟加载也适用于使用 `srcset` 属性的响应式 `img` 元素。该属性为浏览器提供了一个图像文件候选列表。根据用户的屏幕尺寸、显示像素比率、网络状况等,浏览器将选择 **最适合当前情况的图像候选**。像 tiny.pictures 这样的图像优化 CDN 能够 **无需任何后端开发** 就实时提供所有图像候选。
<img src="https://demo.tiny.pictures/native-lazy-loading/lazy-cat.jpg" srcset="https://demo.tiny.pictures/native-lazy-loading/lazy-cat.jpg?width=400 400w, https://demo.tiny.pictures/native-lazy-loading/lazy-cat.jpg?width=800 800w" loading="lazy" alt="...">
浏览器支持
在撰写本文时,还没有浏览器默认支持原生加载。但是,正如我们所述,Chrome 将从 Chrome 76 开始启用此功能。其他浏览器供应商尚未宣布支持。(Edge 是一种例外,因为它很快将 切换到 Chromium。)
您可以使用几行 JavaScript 代码检测此功能。
if ("loading" in HTMLImageElement.prototype) {
// Support.
} else {
// No support. You might want to dynamically import a lazy-loading library here (see below).
}
查看
Native lazy-loading browser support by Erk Struwe (@erkstruwe)
on CodePen.
自动回退到使用低质量图像占位符的 JavaScript 解决方案
大多数基于 JavaScript 的延迟加载库的一个非常酷的功能是 低质量图像占位符 (LQIP)。它利用了这样一个想法,即浏览器立即加载(或者我应该说曾经加载)`img` 元素的 `src`,即使它后来被另一个 URL 替换。这样,就可以在页面加载时加载一个微小的文件大小、低质量的图像文件,然后用一个全尺寸版本替换它。
现在,我们可以使用它来模拟原生延迟加载的 2 KB 范围请求,在不支持此功能的浏览器中实现相同的结果,即具有实际图像尺寸和微小文件大小的占位符。
查看
Native lazy-loading with JavaScript library fallback and low-quality image placeholder by Erk Struwe (@erkstruwe)
on CodePen.
结论
我真的很兴奋这个功能。坦率地说,我还想知道为什么它直到现在还没有得到更多关注,考虑到它的发布即将到来,即使启发式方法中只有一小部分发生变化,对全球互联网流量的影响也将是显著的。
想想看:在不同 Chrome 平台上逐步推出后,`auto` 成为默认设置,**世界上最流行的浏览器很快就会默认延迟加载屏幕外的图像和框架。** 许多编写不当的网站的流量数量不仅会大幅下降,而且 Web 服务器会受到用于图像尺寸检测的微小请求的轰炸。
然后是跟踪:假设许多不知情的跟踪像素和框架将无法加载,分析和联盟行业将不得不采取行动。我们只能希望他们不要惊慌失措,并为每个图像添加 `loading="eager"`,使这个伟大的功能对他们的用户毫无用处。他们应该改变他们的代码,以便在 上面 描述的启发式方法中被识别为跟踪像素。
Web 开发人员、分析和运营经理应该立即检查他们的网站在此功能下的行为以及他们的服务器对 `Range` 请求和 HTTP/2 的支持。
如果预计有任何问题,或者您希望将图像传送优化推向极致(包括自动 WebP 支持、低质量图像占位符等等),图像优化 CDN 可以提供帮助。阅读更多关于 tiny.pictures 的内容!
感谢您的撰写,Erk!
我几乎给我的团队发邮件,提醒他们注意跟踪像素的更改。但我对您的评论感到困惑,
我不理解上述延迟加载的先决条件。首先,只有在启用了 Data Saver 的情况下,`loading="auto"` 的图像才会转换为延迟加载。其次,如果未使用最常见(无处不在?)的跟踪像素/框架实施方法,则框架和图像都不会被延迟加载。
您能解释一下“默认”和“分析… 行业将不得不采取行动”吗?
嗨 Michael,
感谢您的评论。
我指的是 Blink LazyLoad 设计文档中描述的现场试验。我认为,如果一切顺利,延迟加载也将成为禁用 Data Saver 的设备以及移动设备以外其他平台的默认设置。
我所说的“默认”是指省略 `loading` 属性将被视为 `loading="auto"`。
最后,我同意大多数跟踪像素和框架确实会因为其微小的宽度和高度属性而无法进行延迟加载。但是,这肯定不能涵盖所有情况,分析用户将不得不检查是否如此。此外,请考虑那些对用户实际可见的展示广告,它们符合延迟加载的条件,并且可能会比以前更频繁地请求和统计(图像范围请求+完整图像请求)或者比以前更少(根本没有请求的框架)。
感谢您告知我们这一变化!
我相信我并不是唯一一个今天不得不与办公室的后端人员讨论 Range 标头、HTTP2 等问题的人。
Erk,很棒的写作!我也对这个功能感到兴奋,并一直在密切关注它的进展。不过,这个功能不会在 Chrome 75 中上线 - Chrome 75 已经进入 Beta 阶段。这个功能在 Chrome 76 中也没有上线(无需更改标志)。
状态仍然是“开发中”
https://www.chromestatus.com/features#browsers.chrome.status%3A%22In%20development%22
嗨,Christian,
感谢您的评论。不幸的是,原始的跟踪票据无法公开访问。不过,我会直接联系作者,以获取有关状态的更新,并在收到回复后立即编辑这篇文章。
再次嗨,Erk - 你可以在以下位置看到它的当前状态:https://groups.google.com/a/chromium.org/forum/m/#!topic/blink-dev/jxiJvQc-gVg。听起来它现在已经很接近了,在这种情况下我们可以希望它能在 Chrome 77 中上线。
有一个公开的跟踪错误在 https://bugs.chromium.org/p/chromium/issues/detail?id=954323。根据该错误,该功能已推迟到至少 76 版本。但是,由于一些阻碍性票据已安排在 77 版本中,并且 Chrome 平台状态页面也没有列出 76 版本中的该功能,因此我假设该功能将在 77 版本中上线,并相应地更新了文章。
最后,我已经记不清在参与的项目中设置延迟加载解决方案所花费的所有时间了!
感谢您的写作
Erk,很棒的文章!我无法回复您上面的其他讨论,所以发表一条直接评论
“我假设,如果一切顺利,延迟加载也将成为禁用数据节省功能的设备以及除移动设备以外的其他平台的默认设置。”
当然,你可以自由地假设,但你的依据是什么?我认为 Chrome 作为默认行为更改整个网络的图像加载行为极不可能。我的假设是,它现在作为一个通用设置位于一个标志后面,这样它就可以允许广泛的测试,即使没有使用 loading 属性。一旦它“上线”,它将决定每个图像要做什么,而默认情况下,如果图像上没有设置该属性,则不会进行延迟加载。
你的分析备注更令人担忧。尽管我个人讨厌跟踪、广告等等,但浏览器仅仅破坏今天有效的机制是绝对不可接受的。如果你的主张或假设是正确的,数百万的营销人员将面临巨大的问题并立即采取行动。我在任何地方都没有看到谷歌发布过任何暗示此事的声明。
我不能确定你在这两个主张中是对还是错,但我的意思是,你应该绝对确定你说的话是真实可信的,因为这两个主张都对网络产生了巨大的影响。如果你对了,更多人需要尽快知道,如果你错了,信息应该得到纠正。
嗨,Ferdy,
感谢您分享您的想法和担忧。
关于你的第一个观点,我认为我们在这里的意思是相似的:当标志默认启用时,
loading="auto"
设置将隐式成为默认设置。正如你所指出的,Chrome 然后将决定每个图像要做什么,延迟加载只会在某些相当罕见的情况下发生(Chrome Android、数据节省等,如文章中所述)。“Blink LazyLoad Design” 文档列出了在现场试验期间收集的几个指标,例如页面大小、绘制和交互时间、内存使用情况等等。在“核心原则考虑因素”部分,作者指出他们预计页面加载时间、网络数据使用量和内存使用量会得到改善。我假设延迟加载将被考虑用于除 Andriod 以外的其他平台,是因为如果预期的改进被证明是正确的,我无法想到任何不使用该功能的理由。毕竟,网络性能是我们和 Chrome 开发人员日常工作的关键目标,不是吗?
此外,“Blink LazyLoad Design” 文档的最后部分明确指出“如果 Android 的 LazyLoad 成功,那么应考虑将 LazyLoad 用于桌面平台”。
但是,有一些原因会使这种成功面临风险,这让我想到你的第二个观点:兼容性。“Blink LazyLoad Design” 和 “Blink LazyImages” 设计文档表明,作者确实意识到了这些问题并预期会遇到这些问题:广告的错误印象统计、等待 OnLoad 事件的代码中断、等待整个页面加载的功能(例如“另存为”或“打印”)的延迟、跟踪像素的双重提取、大量请求导致的服务器负载,以及在画布上绘制图像时的问题。
鉴于该功能被推迟到 Chrome 版本 > 76,谷歌仍然有足够的时间提出警告,营销人员也有时间做出反应。最后,作者的担忧促使我在我的文章中警告读者这些问题。
@Erk:感谢您回复我。我认为以下引文中的关键词是...
“世界上最流行的浏览器将很快默认延迟加载页面下方图像和框架。”
... 是“将”。这听起来像一个既定事实,而如果我理解正确,它更像是一种个人假设或长期的期望,而不是一个公告。我当然不会排除这是一种可能性,但我认为并希望如此重大的举动会很遥远,并且应该很遥远。或者根本不会发生。
首先,从概念上讲,网络的承诺是向后兼容性。今天有效的应该在明天也有效。只有在非常特殊的情况下才应该打破向后兼容性,例如出于安全原因。这并非抽象理论,网络的很大一部分根本没有维护或维护程度很低,这意味着如果你破坏了它,它将永远被破坏。
默认延迟加载图像可能会破坏很多东西,特别是依赖于 onload 事件以及基于它的各种历史插件的东西。如前所述,也可能存在服务器问题,最糟糕的情况甚至会导致额外的成本。
这里有一个很少有人想到的用户场景:发展中国家的一些人讨厌延迟加载的图像。为什么?考虑到他们的网络速度很慢,他们倾向于加载一个很重的页面并等待它完全下载完毕。假设这需要一分钟。一分钟后,好处是他们可以立即浏览整个页面。相反,延迟加载的图像会导致整个阅读体验糟糕。
我承认,这是一个奇怪的角度,但值得考虑。
除了破坏我称之为“旧网络”的东西(也就是大部分网络)之外,破坏广告和跟踪会导致更多人公开抗议。这是网络上赚钱的方式。触碰或破坏它,准备迎接地狱吧。