Web 平台 API / 响应式图片 · 让浏览器挑对那一张 / 优先级与解码:fetchpriority、preload 与 decoding 待审核 8 / 10
fetchpriority · preload · decode()

优先级与解码:fetchpriority、preload 与 decoding

前几页处理的是「下载哪一张」和「何时开始下载」。对首屏主图,还有两个时间段会推迟它出现在屏幕上:请求发出后在网络队列里排多久,字节到齐后解码要多久。这两段分别由优先级与解码时机决定。

1 · fetchpriority

浏览器给每个请求分配一个优先级,带宽紧张时高优先级先走。据 web.dev 对 Chrome 的描述,图片默认从 Low 起步,布局完成后位于视口内的图片被提升为 High;Chrome 117 起前 5 张大图以 Medium 起步。问题在于「布局完成后」:这之前主图与屏外图片同为 Low,排在样式表与同步脚本之后(核对于 2026-09)。

fetchpriority 让作者直接给出提示,取值为 high、low 与缺省的 auto。首屏主图写 fetchpriority="high",从请求发出起就是 High,不必等布局;轮播里暂不可见的后几张写 low,把带宽让给当前那一张。Chrome 101(2022-04)、Safari 17.2(2023-12)、Firefox 132(2024-10)先后支持(兼容性据 MDN BCD,核对于 2026-09)。

这个属性是相对的提示。所有图片都写 high 等于都没写;它也不改变选哪张候选、不绕过懒加载,loading="lazy" 配 fetchpriority="high" 仍然要等到进入提前量才请求。本页没有实测各档优先级:DevTools 协议给出的请求记录里没有优先级字段,本地 dev server 的响应在 30ms 内全部返回,排队差异无从观察。

2 · preload 与响应式候选

preload scanner 只能发现写在 HTML 里的 <img>。CSS background-image 里的主图、由框架在客户端渲染出的 <img>,都要等样式或脚本执行后才被发现。<link rel="preload" as="image"> 把请求提前到 <head> 解析的时刻。

对响应式图片,preload 不能只写一个 href,否则会预加载一张与 <img> 最终选择不同的图,下载两次。imagesrcset 与 imagesizes 两个属性照搬 <img> 的 srcset 与 sizes,由同一套算法选出同一张候选:

<link rel="preload" as="image" fetchpriority="high"
      imagesrcset="hero-640.jpg 640w, hero-1280.jpg 1280w, hero-1920.jpg 1920w"
      imagesizes="100vw">

两处的候选与 sizes 必须逐字一致,任何一处改了而另一处没改,preload 就变成一次额外下载。imagesizes 不支持 auto:preload 发生在布局之前,没有布局宽度可取。preload 也不认识 <picture> 的 type 与 media 协商,art direction 或格式切换的主图只能配合 <link> 自己的 media 与 type 属性逐条写。

3 · 解码是另一段时间

图片的 load 事件在字节全部到齐时触发,此时位图不一定已经解码。解码在需要绘制时才进行,而绘制在主线程的渲染步骤里。

图 3-1 · 现画一张噪点 JPEG,分别记录 src 到 load 与 load 到 decode() 兑现的耗时。可切换三档尺寸,读数取决于本机 CPU。

作者的机器上(macOS,Chrome 154),一张 4000 × 3000 的噪点 JPEG 从 blob URL 加载,load 在 6ms 触发,随后的 img.decode() 还要 87ms 才兑现;1000 × 750 的同类图是 1ms 与 6ms。在 load 回调里把缩略图换成大图,这 87ms 就会落进下一帧,页面在切换那一刻卡顿,或者短暂显示空白(核对于 2026-09)。

img.decode() 返回一个 Promise,在位图解码完成、可以立即绘制时兑现,解码失败则拒绝。先 await img.decode() 再把图片插入文档或替换 src,切换就发生在一帧之内:

const full = new Image();
full.src = largeUrl;
await full.decode();          // 解码在后台完成
thumb.replaceWith(full);      // 插入时已可直接绘制

decoding 属性是同一件事的声明式版本,取值 sync、async 与缺省的 auto。async 允许浏览器把这张图的解码与其余内容的绘制分开,先画出文字、图片稍后出现;sync 要求它与其余内容在同一帧出现。规范把它定为提示,浏览器可以忽略。实践中,首屏主图不必写(或写 sync,避免主图晚于文字一帧出现),大量缩略图的列表可以写 async,避免一次滚动里几十张图的解码阻塞同一帧。

4 · 图片元素的状态读取

几个只读属性描述图片的当前状态,调试选择与加载时常用:

属性 含义
currentSrc 当前选中候选的绝对 URL;未选定时为空串
complete 没有待处理的请求,且图片已完全可用或已确定损坏
naturalWidth / naturalHeight 经密度校正的天然尺寸(见 x 描述符与像素密度 §2);未加载时为 0
width / height(IDL) 已渲染时为渲染尺寸,未渲染时为属性值

complete 为真不代表加载成功:损坏的图片 complete 同样为真,需要再看 naturalWidth 是否大于 0。