w 描述符与 sizes
流式布局里,图片的 CSS 宽度随视口变化,x 描述符的前提不再成立。w 描述符换了一种陈述方式:作者不再说「这张图适合几倍屏」,而是说「这张图有多少像素宽」,再另用 sizes 说「这张图在布局里有多宽」,由浏览器把两者相除得到密度。
1 · 描述符换算成密度
<img srcset="photo-320.jpg 320w, photo-640.jpg 640w,
photo-960.jpg 960w, photo-1280.jpg 1280w"
sizes="(min-width: 1000px) 300px, calc(100vw - 32px)"
src="photo-640.jpg" alt="…">
640w 的含义是这张图的天然宽度为 640 像素,规范要求它与文件的实际像素宽一致。浏览器拿到 sizes 求出的布局宽度
后,把每个候选的
换算成密度
,之后的选择与上一页的 x 描述符完全相同:取不小于 DPR 的最小密度。布局宽 300px、DPR 2 时,所需密度为 2,候选密度依次为 1.07、2.13、3.2、4.27,选中 640w。
换算还有一个副作用:选中候选的 naturalWidth 等于
除以密度,也就是 sizes 的值本身。图片没有 CSS 宽度时,它会按 sizes 声明的宽度渲染。
2 · sizes 的语法
sizes 是一个逗号分隔的列表,每项是一个媒体条件加一个长度,最后一项可以没有条件:
- 从左到右求值,第一个为真的媒体条件胜出,其后各项不再看。
min-width类条件须按阈值从大到小排列,写反了大视口会先命中小阈值那一项 - 长度允许
px、em、vw与calc()等数学函数,不允许百分比,规范给的理由是避免「相对于什么」的混淆。需要相对视口时用vw - 不写
sizes等同于100vw。这是最常见的浪费来源:一张在桌面上只占 300px 的缩略图,在 1440px 宽的视口里按 1440px 选图
媒体条件在 HTML 里求值,不受 CSS 影响;em 取的是初始字号而不是图片所在元素的字号。
sizes 的媒体条件与 vw 都按 iframe 求值。可切换三种写法并调视口宽,观察命中项与布局实际宽度是否一致。
「顺序写反」一档在视口宽 1000px 以上时暴露问题:(min-width: 600px) 先于 (min-width: 1000px) 为真,sizes 报出 calc(50vw - 30px),而 CSS 在这个宽度已经把图片定为 300px。视口 1100px 时 sizes 报 520px、布局实际 300px,DPR 1 下选中
640w 而非 320w,像素数是所需的 4 倍。
3 · 一份手抄的 CSS
sizes 存在的全部理由是 问题与来历 §2 里那条约束:浏览器在布局之前就要选图。代价是作者必须把 CSS 里的布局规则在 HTML 里再写一遍,且两处没有任何机制保持同步。改了栅格断点而忘了改 sizes,页面照常显示、没有任何报错,只是每张图都按错误的宽度下载。
浏览器完全信任 sizes。图 3-1 里 CSS 宽 300px、sizes 报 400px,DPR 1 下选中 640w,而按真实宽度只需 320w;反过来把 sizes 压到 100px,选中 160w,拉伸到 300px 后明显发虚。拖动 CSS 宽不会触发任何重新选择,因为选择的输入里根本没有它。
sizes 与 CSS 宽度分别可调时的选择结果。可调两者观察只有 sizes 影响选择;切到「复用同一套」后先拉大再缩小 sizes,观察已加载的大图不会被换回。4 · 缓存与环境变化
浏览器在两种情况下会偏离「按 sizes 选」的结果,都是朝更大的方向。
第一种是内存缓存。作者在写本系列的 lab 时最初让所有 lab 共用一套候选 URL,结果发现后加载的 lab 无论 sizes 写多小都选中 1920w。排查后确认:Chrome 154 在选择时会检查候选是否已在内存缓存中,已缓存的更高密度候选直接胜出。同一套 URL 下先加载一张 sizes="1500px" 的图,再插入一张
sizes="10px" 的图,后者 currentSrc 读回 1920w。这对用户是合理的:图已经在内存里,再下一张小的反而多花流量。但它让「这张图选了哪一档」的回答依赖于页面上此前发生过什么,调试时需要用无痕窗口或独立 URL 复现(核对于 2026-09)。
第二种是布局变化。规范允许浏览器在视口变化时重新选择,Chrome 的做法是只升不降:sizes 从 300px 改为 900px,currentSrc 从 320w 换成 960w;再改回 200px,保持 960w。把窗口从宽拖窄的桌面用户因此一直持有大图,这在像素上是浪费,但避免了一次可见的重新加载。
注 · 图 3-1 默认每次调整都新建一套 URL,读到的是不受缓存影响的选择。本系列其余 lab 同样各自生成候选,原因即本节所述。