朋友的相册页被几十张原图拖到转圈:我给图片分了三档、用 srcset 让手机自己挑
朋友旅行回来,拍了上千张照片,让我帮忙做一个简单的相册页,准备发亲戚群里看。他把照片挑出来打包给我,我图省事,网页里直接 <img src> 指向每一张原图,配上瀑布流就发了。结果当晚他很尴尬地转述:有人说“你这页是不是坏了,半天不出来”,有人说“一打开手机流量就告警,赶紧退了”;等我把图换成小缩略图想救场,又有人抱怨“点进去想看清脸,放大全是糊的”。
折腾了一晚上,问题不是相册程序、也不是图床慢,而是我从头到尾只给了一个尺寸的图:要么全原图撑死流量,要么全缩略图糊成一片。把这页修好以后,我把“相册页到底该给多大的图”这件事按踩坑顺序记下来,下次再做带图页面不用重新趟一遍。
一、翻车现场:一屏照片,浏览器其实拉了几十兆
我先在电脑上打开觉得挺正常,照片“唰唰”就出来了。直到在手机蜂窝网络下复现,才发现真相:相册墙一屏几十张图,我每张都指向原图,每张原图好几兆,整页等于一次拉了几十兆。浏览器并不会因为图被显示成小卡片就少下载——它把整张大图拉到本地,再缩成小尺寸。
也就是说,用户看到的是小图,付出的却是原图的流量。手机性能弱一点、网络差一点,页面就一直在转圈;有的人甚至没等图出来就关掉了。
二、改成小图之后,又栽在“点进去看大图”
我当时的第一反应很简单:把图全部换成小尺寸不就好了?换完确实秒开,可新问题立刻来了——相册页是让人“看照片”的,点开任意一张想放大看,结果大图是用那张 300px 的小图强行拉大的,人脸、文字全糊。
这一下我才明白:列表小图和详情大图,本来就该是两个文件,不能用同一张图包打天下。 列表要的是“小而快”,详情要的是“大而清”,需求是相反的。
三、一张图该准备几档:原图、压缩图、缩略图
后来我固定按三档来备图,再没有在这两个极端之间反复横跳:
- 原图:像素和信息完整、体积最大,只给“查看高清”“下载原图”这种入口,不在列表里直接铺;
- 视觉无损压缩图:清理冗余元数据、优化编码,肉眼几乎看不出差,体积小一截,用来做详情页的大图;
- 多级缩略图:固定宽度的小版本,像 100、200、300、600 这几档,宽度够显示就行,专门给相册墙、卡片、头像用。
相册墙用最接近显示尺寸的缩略图,点进去详情用压缩图,需要保存、打印的再给原图,各走各的路,就不会互相拖累。
四、为什么手机上连“小图”都会糊:DPR
在电脑上看,缩略图明明挺清晰,为什么换到手机上还是发虚?这是设备像素比(DPR)在起作用。
网页里写的宽度是逻辑像素,而高分屏(Retina 一类)的物理像素更密。手机上在浏览器里打印 window.devicePixelRatio,大多数是 2,少数是 3。一个 CSS 上 300px 宽的卡片,在 2 倍屏上实际要 600 个物理像素才够锐利;只给 300 的图,被拉到 600 就糊。
所以缩略图不能只按显示宽度备,得按“显示宽度 × DPR”,通常 1x、2x 两档就够;只有详情页主图这种需要放大看的才考虑 3x。档位也别堆太狠,3x 图体积上得快,不分场景全上 3x,反而把刚省下来的流量又吃回去了。
五、让浏览器自己挑:x 给头像,w 给瀑布流
备好了多档图,怎么让浏览器按设备自动选?我给相册页加了 srcset。它有两种写法,一开始我混着用踩了坑,分清楚就很简单。
固定尺寸的(头像、图标)用 x 描述符,按 DPR 选:
<img src="avatar-300.jpg"
srcset="avatar-300.jpg 1x, avatar-600.jpg 2x"
width="120" height="120" alt="头像">
宽度会变的(瀑布流、正文配图)用 w 描述符加 sizes,把每张图的固有宽度和“这张图在页面里占多宽”都告诉浏览器:
<img
src="photo-600.jpg"
srcset="photo-200.jpg 200w, photo-300.jpg 300w, photo-600.jpg 600w, photo-1400.jpg 1400w"
sizes="(max-width: 600px) 100vw, (max-width: 1000px) 50vw, 320px"
alt="旅行照片">
浏览器会结合容器宽度、DPR、网络状况自己挑一张,src 只负责给不支持 srcset 的浏览器兜底。
两条硬规矩我专门记牢:一是 w 和 x 不能写进同一个 srcset;二是用了 w 就一定要写 sizes,不写的话浏览器默认按整屏宽度算,多半会选偏大的图,省了等于没省。至于“窄屏换一种裁剪构图”,那是 <picture> + media 的艺术指导,跟分辨率分档不是一回事,别搅在一起。
六、我让朋友以后就这么传图
修好之后,我把相册页的流程也给他固定下来,免得下次又把原图一股脑塞进来:
- 每张成品都备好原图、一档视觉无损压缩图、几档固定宽度缩略图;
- 相册墙、卡片只放缩略图,详情页放压缩图,“下载/高清”单独给原图;
- 页面用
x或w + sizes写好,让浏览器自己选; - 要替换某张照片(比如换掉一张拍糊的),就用同名文件覆盖,链接不变,页面里的引用不用挨个改。
这一步并不绑死某一家:自己在对象存储上套一层图片处理,按尺寸实时生成各档,也能做到。我为了省事,朋友这些图统一放在图床小镇,上传后直接给原图、无损压缩和 100/200/300/600 几档缩略图的地址,URL、Markdown 都能直接复制,重传同名文件覆盖时链接也不变,不用我自己维护图片处理;但这纯属个人习惯,换成任何能输出多版本、外链稳定的图床或对象存储,做法和结果都一样。
“同名覆盖、链接不变”这件事我之前专门踩过:客户说文档里的截图还是旧的,我重传了三遍才发现自己一直在“新增”而不是“覆盖”,过程记在《客户说文档里的截图还是旧的,我重传了三遍才发现:我一直在“新增”而不是“覆盖”》里。另外相册页的图如果要嵌到别的网页、或者顺手发邮件,记得留意图源那边的防盗链白名单,否则页面里能看、换个地方就裂,这点我在《配了防盗链想防别人盗图,结果先把自己的订阅者全挡了:一晚 Referer 折腾记》里也验证过。
七、怎么确认它真的变快了
改完我没凭感觉收工,而是实打实验证了一遍:
- 手机和电脑各打开一次,相册墙秒出、点开详情依旧清晰;
- DevTools 的 Network 里看实际下载的是哪一档、整页拉了多少,切设备模拟不同 DPR,看选中的尺寸会不会变;
- 控制台打印图片的
currentSrc,能直接看到浏览器最终挑了哪个文件; - 用 Network 节流模拟弱网,再看相册墙是不是仍然先出小图;
- 无痕窗口打开一次,避免本地缓存造成“已经很快”的错觉。
写在最后
回过头看,相册页翻车和救场,本质上都是一句话:显示多大就给多大,再按手机的 DPR 多备一档,原图只留给真正需要高清的地方。 把“列表小图、详情中图、高清原图”分清楚,再用 srcset 把选择权交给浏览器,秒开和清晰就不必二选一了。
浙公网安备 33010602011771号