你一定见过这种页面:文字已经能读了,图片才慢慢加载出来,然后整段内容被往下推了一大截。手指正要点某个链接,结果点到了别的东西。这个指标叫累积布局偏移(CLS),罪魁祸首通常是图片。
浏览器在等什么
<img> 在没有宽高信息时,初始尺寸是 0×0。图片下载完成、解码出真实尺寸后,浏览器才重新排版。如果图片在首屏,这一下位移就很明显。
修法不是「加载快一点」,而是提前告诉浏览器要占多大地方。
<img src="/cover.jpg" width="1200" height="750" alt="项目封面" />
只要给了 width 和 height,浏览器会按比例预留空间,即使图片还没到,位置也是稳的。这一点对响应式图片同样成立:CSS 的 width: 100%; height: auto 会保持比例。
比例容器更适合卡片
列表页的封面往往来自不同尺寸的原图,直接依赖原图比例会让卡片高度参差不齐。用一个固定比例的容器把视觉统一:
.card__cover {
aspect-ratio: 16 / 10;
overflow: hidden;
background: var(--bg-elevated);
}
.card__cover img {
width: 100%;
height: 100%;
object-fit: cover;
}
object-fit: cover 保证不变形,background 提供一个中性底色,图片没到时也不会是刺眼的白块。
响应式图片别只写 srcset
只给 srcset 而不给 sizes,浏览器默认按 100vw 选择,移动端可能拉到一张桌面大图。
<img
src="/cover-800.jpg"
srcset="/cover-400.jpg 400w, /cover-800.jpg 800w, /cover-1600.jpg 1600w"
sizes="(max-width: 768px) 100vw, 360px"
width="1200"
height="750"
alt="项目封面"
loading="lazy"
/>
sizes 描述的是「图片在布局中占多宽」,浏览器据此挑选合适的那张。这一步做对,移动端首屏图片体积往往能减半。
首屏图不要懒加载
loading="lazy" 是默认好习惯,但首屏大图加懒加载反而会拖慢最大内容绘制(LCP)。正确的做法是区分:
- 首屏主图:
loading="eager"+fetchpriority="high" - 其余图片:
loading="lazy" - 弹窗、画廊里默认不显示的图:等到打开时再插入 DOM
用 CSS 占位,而不是占位图
没有封面时,很多人会放一张灰色的 placeholder.png。这多了一次请求,还多了一次布局计算。用渐变加文字更轻:
.card__cover--empty {
display: grid;
place-items: center;
background: linear-gradient(135deg, rgba(122, 162, 247, 0.18), rgba(167, 139, 250, 0.12));
color: var(--text-secondary);
font-family: var(--font-mono);
}
验收方法
不要靠感觉。打开 DevTools 的 Performance 面板录制一次加载,看 Layout Shifts 轨道;或者直接看 Lighthouse 的 CLS 分数。目标定在 0.05 以内,超过 0.1 就属于需要修的范围。
还有一个更朴素的检查:把网络限速到 Slow 3G,刷新页面,如果内容在你眼前跳了一次,那就是有问题。这个测试不需要任何工具,但非常接近真实用户的感受。