decoding async 实测 27 个首页:704 张图这么写,只有一张要 sync
decoding async 告诉浏览器下一帧不用等这张图解码。2026-09-07 抓的 27 个首页共 1560 个 img 元素里,705 个声明了解码方式,其中 704 个写 async,只有一张要 sync。没有人写 auto —— 因为 auto 本来就是默认值。

实测 · 2026-09-07 · 27 个首页 · 1560 个 img 元素 · 一个从来不说不的属性
样本 / 口径:2026-09-07 当天对 30 个首页各发一次 GET,桌面 Chrome UA,跟随跳转,不执行 JavaScript,出口在日本。stackoverflow.com、medium.com、www.reddit.com 三个站返回 403,剔除后纳入 27 个。只解析投递下来的 HTML 里的 <img> 元素,脚本后来挂上去的图不在这些数里。
1560 个图片元素里,705 个带了 decoding 属性,其中 704 个写的是 decoding async。整个面板只有一张图要 sync,而且那是有意的。27 个站里 15 个用了这个属性,用 loading 的却有 25 个——大家先伸手去拿的,是那个决定「下不下载」的属性,不是这个决定「绘制等不等」的。
出海站为什么会同时加上这三个属性
多半是照着某份性能清单加的。上线前跑一遍打分工具,清单里并排列着 loading="lazy"、decoding="async"、fetchpriority="high",于是三个一起加上。加完页面没变慢,也说不清哪个起了作用。
它们其实管的不是同一段。分开看一次,后面这些数字才有意义。
| 属性 | 管哪一段 | 省的是什么 |
|---|---|---|
| loading | 下不下载 | 流量 |
| fetchpriority | 先下哪一个 | 等待顺序 |
| decoding | 绘制等不等 | 毫秒 |
只有第一个能让一次请求彻底不发生。第三个从头到尾不影响网络,它只挪动了一个步骤的先后。
怎么测的
把每个存下来的首页扫一遍 <img> 元素,每个元素读三个属性:decoding、loading、fetchpriority,值统一转小写。按元素计数,不按图片文件去重——同一个 logo 在页眉和页脚各出现一次就算两个,这才是浏览器要面对的数。
# 同样三个数,拿去数自己的首页
curl -sL -A 'Mozilla/5.0' https://example.com/ > home.html
grep -o '<img[^>]*>' home.html | wc -l
grep -o '<img[^>]*>' home.html | grep -c 'decoding='
grep -o '<img[^>]*>' home.html | grep -c 'loading='
第一版扫出来是错的,错法值得交给你。loading= 这个模式会顺手命中 data-loading=,而 www.figma.com 有 20 个元素同时带着 data-loading="true" 和 loading="eager"。脚本读到的是框架自己的记账属性,于是记下「20 个元素的 loading 值是 true」——一个规范里根本不存在的值,表还做得挺整齐。把模式前面加上「不能被连字符或字母挨着」的限制才修好,figma 实际上是 20 个 loading="eager"。上面那三行命令有同样的坑:grep -c 'decoding=' 一样会把 data-decoding= 数进去。
三条局限。一次请求,只取首页。全程不执行 JavaScript,所以框架在浏览器里才挂上去的图整批看不见——这个面板里单页应用不少,漏掉的是实实在在的一块。另外本篇量的是声明不是结果:没测过任何一个页面的解码耗时、绘制耗时或 LCP,所以这里没有一句话能说明这个属性有没有帮上忙。
1560 张图里 705 张声明了解码方式
这个属性出现在 45% 的图片元素上。它按站分得比按图分得更干脆:4 个首页给自己每一张图都加了,12 个首页一次都没用过。
| 2026-09-07 | 数量 |
|---|---|
| img 元素总数 | 1560 |
| 带 decoding | 705 |
| 值为 async | 704 |
| 值为 sync | 1 |
| 值为 auto | 0 |
| 带 loading | 1099 |
没有人写 auto,这件事有机械上的原因:它本来就是你不写时得到的东西。HTML 标准写明这个属性的「missing value default and invalid value default are both the Auto state」(HTML Living Standard,2026-09-07 访问),所以打上去等于没打。它实际上是个两值属性,而两个值里有一个在 1560 个元素里只出现了一次。
每张图都加的 4 个站是 nextjs.org(57 / 57)、arstechnica.com(60 / 60)、www.cloudflare.com(66 / 66)、www.netlify.com(23 / 23)。这个形状从外面看就是一个组件库:一个图片组件,属性焊在里面,到处都带着,没有人为哪一张图单独做过决定。数量最多的是 www.framer.com,166 张里 164 张带。
decoding async 到底做了什么,又没做什么
它是一个关于绘制的提示,对网络没有任何影响。MDN 的说法是,这个属性提示浏览器「whether it should perform image decoding along with rendering the other DOM content in a single presentation step that looks more 'correct' (sync), or render and present the other DOM content first and then decode the image and present it later (async)」,并且给了一句更好用的:「In practice, async means that the next paint does not wait for the image to decode」(MDN,img 元素,2026-09-07 访问)。
MDN 同时很坦白地说效果小到不容易看见:「It is often difficult to perceive any noticeable effect when using decoding on static <img> elements」,因为图片本来就是各自独立取回和处理的;不过「the blocking of rendering while decoding happens, while often quite small, can be measured — even if it is difficult to observe with the human eye」。
loading 决定这些字节要不要花出去,decoding 只决定下一帧等不等它。一个省流量,一个省毫秒。
25 个站用 loading、15 个站用 decoding,差的就是这一层。把屏幕外的图往后推能省掉一次下载,MDN 说 lazy「avoids the network and storage bandwidth required to handle the image until it's reasonably certain that it will be needed」。解码提示省不掉任何东西,它只是把一个步骤换了个位置。
decoding async 和 loading lazy 实际是怎么搭的
两个属性挂在同一个元素上,所以真正有意思的是组合,不是各自的总数。面板上出现过的搭法全在这里。
| decoding | loading | 图片数 |
|---|---|---|
| async | lazy | 556 |
| 没写 | lazy | 432 |
| 没写 | 没写 | 418 |
| async | eager | 105 |
| async | 没写 | 43 |
| 没写 | eager | 5 |
| sync | eager | 1 |
占大头的是 async 加 lazy,556 张。这个组合按定义就接近冗余:一张被懒加载的图在屏幕外,本来也拦不住当前这一帧。它不花什么成本,而且它正是「组件默认两个都设上」会产生的样子,不是每张图想过。
真正让这个提示有活干的是那 105 张 async 加 eager。eager 的意思是现在就取,位置靠上,就在读者正等的那一帧里。在那儿告诉浏览器「别为这张图的解码卡住这一帧」是一句有内容的指令。另外 43 张带了解码提示却完全没写 loading,等于默默继承了 eager 的默认值。
还剩 418 张——占面板 27%——两个属性都没有。那是浏览器在两个维度上的默认行为。10 个站用 loading 但从不用 decoding;2 个站两个都不用,而这两个站的图本来就极少(news.ycombinator.com 有 2 张,www.wikipedia.org 有 1 张)。
那唯一一张要 sync 的图
1560 个元素里只有一个写了 decoding="sync",在 astro.build 上,而且不是手滑。那是首屏的背景图,身上三个属性彼此是自洽的:
<img src="/_image?href=...HeroBackground...&w=256&h=214&q=10&f=webp"
alt loading="eager" decoding="sync" fetchpriority="high"
width="256" height="214">
三个连起来读,意图很清楚:立刻取,给高优先级,并且和别的东西在同一步里画出来,而不是让页面先出来、它随后再补上。这张图请求的尺寸是 256×214、质量 10——一张又小又压得很狠的占位图,解码成本极低,这才让「要求同步解码」这件事显得合理而不是鲁莽。
这么做在那个页面上是不是真的更好,我们没测。它是一个页面上的一张图,老实的说法是:有人为一个具体元素做了一个具体选择,而面板上其余的站不需要做这个选择。
这对你意味着什么
这个属性很便宜,风险也低,而这两件事是同一枚硬币:到处加几乎不花钱,收益在大多数已经加了的图上也差不多是零。它真正管用的地方很窄。
- 用上面那三行命令数一遍自己的首页,然后读比例。如果每张图都有
decoding,那是组件在设,没有人为哪张图单独选过。 - 只看首屏那些图——带
loading="eager"的,和干脆没写 loading 的。本次那 105 张加 43 张,才是这个提示能改变点什么的位置。 - 如果你在精简标签,懒加载的图上可以不写
async。本面板 556 张属于这种,等于在叮嘱浏览器别等一个它本来就没在等的东西。 - 别写
decoding="auto"。不写和写错都会落到同一个状态,打上去纯属多敲几个字。1560 张图里 0 张这么写。 - 只有说得出理由才用
sync,像 astro.build 那样:一张又小又便宜的高优先级图,你不希望它在版面出来之后才蹦进来。
这些都不是排名因素,当成排名因素来排优先级就本末倒置了——决定一张图取不取得到、有没有尺寸、有没有说明的那几个属性值钱得多,它们在 图片 seo 的六个属性 里。同一批站在「取不取」这一侧怎么分,数在 27 个首页的图片懒加载实测。想看引擎在任何脚本跑之前拿到的是哪一份页面,看看 QueryWin 从一个页面里读出什么。
常见问题
你们是怎么测的
2026-09-07 当天,每个首页一次 GET,桌面 Chrome UA,跟随跳转,出口在日本,正文落盘。把投递 HTML 里每个 <img> 元素匹配出来,读 decoding、loading、fetchpriority 三个属性,正则做了锚定,保证 data- 开头的属性匹配不上。按元素计数。30 个站里 3 个返回 403 已剔除,剩 27 个。全程没有执行 JavaScript。
decoding async 和 loading lazy 有什么区别
作用的阶段不一样。loading="lazy" 把下载推迟到图片快进视口时,省下的是可能永远不用花的字节。decoding="async" 完全不碰下载,它说的是下一帧不必等这张图解码完。两个一起写很常见——本次有 556 张——但在一张屏幕外的图上,解码提示已经没什么可改的了。
加了 decoding async 能提升 Core Web Vitals 吗
这次没测,本篇的口径也回答不了这个问题。有据可查的部分很窄:MDN 说解码期间的渲染阻塞「often quite small」,但「can be measured」。就本次收集到的证据,只能说 27 个站里 15 个认为值得设,12 个认为不值得。
出海站要不要给每张图都加
本面板有 4 个站就是这么干的,看不出有什么害处。但它在那 556 张懒加载的图上基本是空转。组件白送就留着;要是你打算手工一张张加,那就只加首屏那些真正会被立刻取回的。
为什么没人写 decoding="auto"
因为它本来就生效着。标准把 Auto 同时定成「不写时的默认值」和「写错时的默认值」,所以没写属性的图、写错属性的图,和把 auto 拼对了的图,最后落在同一个状态。本面板 1560 张图里 0 张写它。


