这篇文章进入了性能优化的另一个大头—— “图片性能优化(Image Performance)” 。
图片通常是网页上体积最大、最占带宽的资源。文章的核心思想是: “在不牺牲肉眼可见画质的前提下,通过各种手段让浏览器下载最少的图片字节数” 。
主要内容可以概括为以下四个核心技术维度:
1. 响应式图片与尺寸优化(Dimensions & Density)
-
解决方案:
srcset 与 sizes 属性:用 srcset 提供不同分辨率/宽度的图片候选(如 1000w 表示固有宽度 1000 像素),用 sizes 告诉浏览器图片在不同视口下的布局宽度。让浏览器自己计算并下载最合适的那张。<picture> 元素:比 <img> 更高级。可以用 <source media="..."> 强行命令浏览器在特定屏幕尺寸(或高 DPR)下切换不同尺寸、甚至不同裁剪方向的图片。
2. 现代文件格式与压缩(Formats & Compression)
3. 服务端动态投喂(Content Negotiation)
- Accept 请求头:浏览器在请求图片时,会在 HTTP 请求头带上
Accept: image/avif,image/webp,...。 - 自动优化:大厂的高级做法是在后端服务器或图片 CDN 上做拦截。如果发现用户的浏览器支持 AVIF,即便 HTML 里写的是
src="pic.jpg",后端也会自动返回 pic.avif 格式。这样可以保持前端 HTML 代码的简洁。
4. 浏览器渲染与加载控制(Loading & Decoding)
loading="lazy"(延迟加载) :极度重要。加上它之后,不在首屏(视口外)的图片先不下载,等用户滚动到附近时才加载。瞬间省下大量首屏带宽。
decoding="async"(异步解码) :告诉浏览器在后台默默去把图片的数据流解码成像素,不要卡住主线程渲染其他文字或布局,对高分辨率大图很有用。
总结: 前一章讲的是用“资源提示”来调控下载时机,而这一章讲的是用“自适应技术”来控制资源本身的大小。大厂在做图片优化时,通常是 loading="lazy"(非首屏延迟) + 现代格式(AVIF/WebP) + 响应式尺寸(srcset)组合出击。
提问
DPR这个是什么?为什么不同屏幕同一张图要求不一样,怎么计算这种不一样找到合适的点
这又是前端性能优化和移动端适配中一个极其核心的考点。大厂面试官非常喜欢用它来测试你对“移动端物理像素和逻辑像素”的底层理解。
我们把 DPR 拆开来聊透,看看它到底是怎么折腾前端开发者的,又是怎么计算和优化的。
一、 DPR(Device Pixel Ratio)到底是什么?
DPR 的全称是 设备像素比。它的公式非常简单:
DPR=逻辑像素(CSS 像素)物理像素(设备像素)
💡 为什么会有这个东西?
在 2010 年以前(iPhone 3 时代),1px 的 CSS 逻辑像素 = 1 个物理发光点,此时 DPR=1。 但 2010 年 iPhone 4 带来了 Retina(视网膜)屏幕,它的物理尺寸和 iPhone 3 一样大,但苹果塞进去了两倍的物理发光点。
如果这时候继续让 1px CSS 像素对应 1 个物理发光点,整个网页在 iPhone 4 上看起来就会直接缩小到原来的一半。
所以苹果发明了网格映射:在 iPhone 4 上,让 1px 的 CSS 逻辑像素,横向对应 2 个物理像素,纵向对应 2 个物理像素。也就是说,用 2×2=4 个物理像素去渲染 1 个 CSS 像素。 这样网页大小没变,但是文字和线条变得极度清晰、没有锯齿。这就叫 DPR=2。
二、 为什么不同屏幕同一张图要求不一样?
这就是“为什么图片会糊”或者“为什么浪费流量”的根源。
图片是由一格一格的“像素位(固有尺寸)”组成的。 假设你在页面上写了一个 width: 500px; height: 500px; 的容器。
-
在 DPR = 1 的普通电脑屏幕上: 容器实际需要 500x500 个物理像素。你给他一张 500x500 像素的图片,一比一完美对应,画面很清晰。
-
在 DPR = 2 的高端手机屏幕上: 因为 DPR=2,这个 500x500 的 CSS 容器在手机屏幕上实际上占用了 1000x1000 个物理发光点。
- 如果你还给它 500x500 的图:图片只有 25 万个像素信息,却要填满 100 万个发光点,浏览器只能把图片拉伸放大,图片立刻就变糊了。
- 如果你给它 1000x1000 的图:图片像素和物理发光点一比一对应,图片在手机上就非常锐利、清晰。
-
在 DPR = 3 的超级旗舰机上: 这个容器实际需要 1500x1500 个物理发光点。你得给它 1500x1500px 的图才最完美。
⚠️ 矛盾点来了:
那我直接所有人统一发 1500x1500px 的超清大图不就行了? 不行! 如果 DPR = 1 的老电脑用户下载了 1500x1500px 的图,他的屏幕只能显示 500x500,浏览器会做下采样(压缩),用户肉眼完全看不出区别,却白白多下载了 9 倍的文件体积(面积比是 3×3=9),浪费了大厂昂贵的 CDN 流量和用户的手机话费。
三、 怎么计算和找到这个“合适的点”?
大厂的核心优化思路是:自适应投喂 —— 算好设备的 DPR 和容器宽度,它要多少,我给多少。
业界有两种主流的落地计算和实现方案:
方案 1:现代 HTML5 标签自动计算法(Srcset + Sizes)
让浏览器自己去算。你只要把各种尺寸的图片备好,在代码里给提示:
HTML
<img
src="img-500.jpg"
sizes="(min-width: 768px) 500px, 100vw"
srcset="img-500.jpg 500w, img-1000.jpg 1000w, img-1500.jpg 1500w"
alt="示例"
>
浏览器的计算逻辑:
- 某个用户用的是 iPhone 13,屏幕宽 390px,DPR=3。
- 浏览器看
sizes:此时视口小于 768px,图片宽度为 100vw(即占满屏幕 390px)。 - 浏览器开始计算物理像素需求:390px×3(DPR)=1170 物理像素。
- 浏览器看
srcset 候选:有 500w、1000w、1500w。1000w 有点不够(会糊),最接近且能满足的是 1500w。 - 结果:浏览器自动决定下载
img-1500.jpg。
方案 2:图片 CDN 动态裁剪法(大厂最常用)
大厂(如阿里、腾讯、字节)都有专门的图片 CDN 服务。前端可以通过 JS 算出当前设备的 DPR,然后拼接 URL 参数去向后端要图。
JavaScript
// 1. 获取当前设备的 DPR
const dpr = window.devicePixelRatio || 1;
// 2. 假设在当前布局下,图片容器的 CSS 宽度是 300px
const containerWidth = 300;
// 3. 计算出最完美的物理图片宽度
const targetWidth = containerWidth * dpr; // 如果 dpr 是 2,就是 600
// 4. 动态拼接阿里云/腾讯云等 CDN 裁剪参数
const imageUrl = `https://cdn.example.com/product.jpg?x-oss-process=image/resize,w_${targetWidth}`;
// 5. 把这个 URL 赋给 img 标签
myImage.src = imageUrl;
追问:人眼是有极限的(大厂的省钱绝招)
有些人可能会继续深挖:“既然 DPR = 3 需要 3 倍图,那我们需要无限追求高清吗?”
- 大厂的标准答案:不需要,DPR = 2 是性价比最高的点。
- 科学研究表明,在正常观看手机的距离下,人眼几乎无法分辨 DPR = 2 和 DPR = 3 的画质区别。
- 因此,大厂为了节省昂贵的带宽成本,通常在计算时会做一个截断优化:如果是 DPR >= 2 的设备,一律只返回 2 倍尺寸的图片(把目标上限卡在 DPR=2)。这样既保证了画面绝不糊,又砍掉了 3 倍大图带来的巨大流量开销。
阅读原文