ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

图片加载性能优化:从3.2秒到600ms全链路实战

图片加载性能优化:从3.2秒到600ms全链路实战 在某次页面改版上线之后客服群里开始冒出一种熟悉又不那么好受的反馈商品列表页图半天刷不出来。我第一反应是打开自己的浏览器刷新了一下——秒开Network 面板里图片全部来自磁盘缓存总耗时不到 80ms。这就是图片加载缓慢问题最让人头疼的地方它几乎从不在开发者自己的机器上复现。你用着千兆宽带、i7 处理器、系统里躺着上一次访问留下的缓存而用户可能在早高峰的地铁上、用着一台三年前的中端安卓机、信号只有两格。图片加载缓慢从来不是一个绝对的性能指标而是用户当前环境与你的图片交付方式之间的匹配度问题。这篇文章想聊的就是这套匹配关系怎么拆开看一张图从服务器磁盘到用户屏幕上亮起来中间要经过哪些环节每个环节分别可能吃掉多少时间以及在实际项目里我是怎么一步步把 3 秒多的图片加载压到 600ms 以内的。内容偏实战涉及排查方法、格式压缩参数、加载策略配置和服务端缓存前端、后端、运维都能从中找到自己那一段的活。1. 一张图片从服务器磁盘到屏幕中间隔着六道关卡1.1 把加载这个词拆成可测量的六个阶段大部分人说的图片加载慢在浏览器眼里其实是六个独立阶段的总和任何一段拖后腿都会表现为图出不来。按时间顺序排DNS 解析、TCP 与 TLS 握手、请求排队与调度、等待首字节TTFB、内容下载、解码与绘制。前四个阶段跟图片本身大小几乎无关纯粹是链路和服务器的问题后两个阶段才真正跟图片格式、体积、分辨率挂钩。这个区分非常重要因为它们的优化手段完全不重叠。我见过太多团队一遇到图慢就去压图片把一张 800KB 的图压到 200KB结果发现加载时间只从 3.2s 降到 2.9s——因为那 2.5s 里有 2s 花在了握手和 TTFB 上。反过来也有些团队服务器配置得漂漂亮亮TTFB 只有 60ms但页面上一张 5MB 的未压缩 PNG 直接把用户拖死。所以排查的第一步永远是先定位哪一段在吃时间而不是凭直觉动手。DNS 解析的典型耗时在 20ms 到 120ms 之间取决于本地 DNS 缓存和递归服务器距离TCP 握手是 1 个 RTTTLS 1.3 在此基础上再加 1 个 RTTTLS 1.2 要加 2 个。RTT 这个数字很关键同机房内网通常 1-5ms跨省 20-40ms跨大洲 150-300ms。也就是说如果你的图片服务器放在海外而用户在境内光是握手就要花掉 300-600ms这还没开始传任何数据。1.2 各阶段的典型耗时与影响因素对照把上面这些数字整理成一张表排查时可以直接对着填阶段典型耗时主要影响因素可优化手段DNS 解析20-120msDNS 缓存、递归服务器位置dns-prefetch、减少域名数量TCP/TLS 握手1-3 个 RTT服务器地理距离、协议版本就近接入、TLS 1.3、连接复用请求排队0-500ms并发连接数、域名分片策略HTTP/2 多路复用、减少并发请求TTFB50-500ms服务器处理逻辑、缓存命中率静态化、CDN 边缘缓存内容下载与体积成正比图片体积、用户带宽压缩、格式转换、分辨率适配解码与绘制5-200ms图片像素总量、设备性能限制展示尺寸、避免超大图缩放下载这一段可以直接算一张 300KB 的图片等于 2.4Mbit在 10Mbps 的实际下行速度下需要 240ms在弱网 1Mbps 下需要 2.4 秒。而 4G 网络的实际下行通常在 5-20Mbps 之间波动弱网场景信号弱、基站拥塞会掉到 100-500Kbps。这就是为什么同一张图有人在办公室里秒开有人在电梯里要等半分钟——带宽差了 100 倍体积不变的前提下时间就差了 100 倍。1.3 为什么我本地很快这个判断毫无参考价值开发环境几乎把所有不利因素都屏蔽掉了。浏览器缓存里躺着上次的图片本地服务器 RTT 小于 1ms公司网络是千兆专线机器是顶配。这四个条件里只要有一个不成立结论就会翻转。判断图片加载是否存在问题必须至少满足两个前提禁用缓存DevTools 的 Network 面板勾上 Disable cache和限速Chrome 的 Network Throttling 选 Fast 3G 或自定义 1.6Mbps。我通常还会额外降一档到 Slow 3G400Kbps来观察最差情况下的表现因为在弱网下暴露出来的问题往往是架构层面的真问题而不是再压一压图能解决的。还有一个经常被忽略的维度设备的解码能力。一张 4000×3000 的 JPEG解码成位图占用的内存是 4000×3000×4 字节接近 48MB。低端安卓机的可用内存本来就紧张同时解码几张这样的大图很容易触发系统回收或者直接卡顿。这个开销在开发机上完全感知不到但在千元机上就是实打实的几百毫秒白屏。2. 定位问题从现象倒推原因而不是直接改代码2.1 先分清是三种慢里的哪一种图片加载慢这个描述太粗了落到具体场景至少要分成三类处理方式完全不同。第一类是首屏慢页面打开后主视觉图或者列表前几张图迟迟不出现其余元素都渲染完了。这通常指向首屏图片的优先级没有设置好或者被懒加载逻辑误伤也可能是首屏图体积过大。第二类是单张图慢整个页面正常就某几张图特别慢刷新几次现象一致。这类问题往往出在特定的图片本身——某张图是未经处理的原图、某个图片 URL 走了不同的域名、或者某张图所在的 CDN 节点缓存没有预热。第三类是滚动慢往下滚动时新出现的图片加载迟滞出现明显的空白占位。这是懒加载触发时机设置得太晚或者预加载距离rootMargin太小导致的。这三种现象在 DevTools 里长得完全不一样先分类再动手能省下大量瞎改的时间。2.2 一份真实的排查记录从 3.2 秒到 600 毫秒说个具体案例。一个电商列表页用户在 4G 下反馈图片要 3 秒以上才出来。我先用 Fast 3G 限速复现Network 面板过滤 Img按耗时排序看到了这样的分布最长的一张图 3.2s其中 TTFB 显示 1.4s内容下载 1.5s剩余是排队和其他开销。TTFB 1.4s 这个数字立刻暴露了问题——图片是静态资源正常情况下 TTFB 应该在 50-200ms 之间1.4s 说明请求打到了源站而不是 CDN源站还在做实时裁剪和格式转换。去查配置发现 CDN 的缓存规则里带查询参数的图片 URL?w750h750被配置成了不缓存因为运维担心查询参数组合太多会撑爆缓存空间。这就导致每一张不同尺寸的图都要回源处理一次。修复动作有两步把尺寸参数改成路径形式/750x750/xxx.jpg并在 CDN 上开启忽略查询参数排序的缓存策略同时把源站处理好的图片结果回写到对象存储。改完之后 TTFB 从 1.4s 降到 90ms。剩下的 1.5s 下载耗时问题在图片本身原图是 1200×1200 的 JPEG用在 375px 宽的手机上实际展示尺寸只有 375×375但代码里没做分辨率适配直接给了 2 倍图。同时质量参数是默认的 100单张 480KB。改成按 DPR 输出 1x/2x 两套375 和 750质量参数降到 82格式走 WebP单张降到 68KB。1.5s 直接变成 180ms。最终端到端从 3.2s 降到 600ms 左右其中还包含了一次 DNS 和握手。整个过程中真正压图片只贡献了不到一半的收益剩下的来自 CDN 缓存和连接层的修复。这个比例在真实项目里非常典型。2.3 现场采集数据要用哪些工具浏览器端我用得最多的是 DevTools 的 Network 和 Performance 面板。Network 面板要打开Big request rows和Show overview把 Timeline 拉宽一眼就能看出哪个时间段网络是空闲的、哪个时间段在密集下载。右键某一列还能把 TTFB、Content Download、Waiting 这些细分字段加进表格。Performance 面板主要看两件事图片解码和布局抖动。录制一段滚动操作在 Main 线程里找到 Decode Image 的任务块如果单张图的解码超过 30ms就说明这张图的像素量对目标设备来说太大了。另外找 Layout 里的紫色块如果图片加载完成后触发了一次大范围重排那就是没有提前声明宽高导致的。线上数据靠 RUM真实用户监控。这里有个关键点一定要采集elementtiming或者 PerformanceObserver 的largest-contentful-paint里那张图的信息包括它的 URL、实际下载耗时、以及用户当时的网络类型。只看平均值会把问题全掩盖掉——P50 可能只有 400ms但 P95 是 4s而恰恰是这批 P95 用户在投诉。我习惯按网络类型4G/3G/WiFi和设备内存分桶看 P75/P95弱网桶的数据才是优化的真正靶子。3. 体积是第一性问题格式选择与压缩参数的取舍3.1 四种主流格式各自的适用边界格式选错后面所有优化都是在错误的地基上盖楼。目前主流的四类格式定位差别很大JPEG适合照片类、色彩连续、不需要透明通道的图。兼容性最好压缩率中等不支持透明度。质量参数在 75-85 之间时视觉上几乎看不出损失但体积比质量 95 能小 50%-60%。PNG是无损格式适合图标、线条图、需要透明通道的图。照片类图片用 PNG 是灾难同样内容体积可能是 JPEG 的 3-5 倍。如果一定要用走 PNG-8 加颜色量化256 色以内能压到原来的 20%-30%。WebP目前是性价比最高的选择有损模式比同质量 JPEG 小 25%-35%还支持透明和动图。兼容性已经很好了现代浏览器全覆盖只需要给极老的浏览器留个 fallback。AVIF压缩率最高同质量下比 WebP 还能小 20%-30%支持 HDR 和透明。代价是编码速度慢通常是 WebP 的 5-10 倍而且部分设备的解码成本更高。适合对体积极度敏感、且能接受离线预编码的场景比如首屏主视觉。实际项目里的策略通常是默认 WebP首屏大图提供 AVIF 并用 picture 标签降级图标走 SVG照片类坚决不用 PNG。这个组合能覆盖 95% 的场景。3.2 压缩参数怎么定几个容易配错的开关以 JPEG 和 WebP 为例真正影响体积的几个参数是质量quality不是越高越好。经验值是 WebP 用 75-80JPEG 用 78-85。再往上体积陡增而肉眼无感。色度采样chroma subsampling4:2:0 相比 4:4:4 体积能小 30%-50%代价是色彩边缘精度下降。照片类无脑用 4:2:0含细密文字或彩色线条的图要谨慎。渐进式progressiveJPEG 开启后图片会从模糊到清晰逐层渲染用户感知上的等待时间明显缩短虽然总字节数可能略增 5% 左右但体验更好。元数据剥离数码相机拍的原图里可能嵌了几百 KB 的 EXIF 和缩略图剥掉它几乎零成本减体积。用jpegoptim --strip-all或者 sharp 的withMetadata(false)都能做到。如果你用 ffmpeg 做批处理一条常用的命令大致是这样cwebp -q 78 -m 6 -mt -metadata none input.jpg -o output.webp # JPEG 批量压缩并剥离元数据 jpegoptim --strip-all --max82 --all-progressive ./images/*.jpg-m 6是压缩方法级别数字越大越慢但压得越狠离线批处理用 6实时处理用 4 就够了。3.3 尺寸适配别再把桌面大图塞给手机比格式更容易被忽略的是尺寸。一张在 1440px 宽的桌面上展示的图和一张在 375px 宽手机上展示的图内容完全一样但需要的像素量差了将近 15 倍。如果代码里只有一套 1920px 的图手机用户就要下载并解码 15 倍于它需要的像素数据。正确的做法是按 DPR 输出多档尺寸。展示宽度 400px 的图至少准备 4001x和 8002x两档3x 设备用 1200。再往上就收益递减了因为人眼对高 DPR 下的细微差异不敏感而带宽和内存是实打实的成本。一个实操细节裁剪和压缩的顺序。一定是先裁剪到目标尺寸再压缩。先压缩再裁剪会导致二次编码损失画质下降且体积不划算。在服务端用 sharp 处理的话链式调用里resize必须写在toFormat之前。await sharp(input) .resize(800, null, { withoutEnlargement: true }) .webp({ quality: 78, effort: 4 }) .toFile(output);withoutEnlargement这个选项很实用能防止把小图强行放大——那种情况只会白白增加体积并让画面变糊。4. 加载策略什么时候开始加载按什么顺序加载4.1 懒加载用错的三种典型症状懒加载是图片优化里被滥用最严重的一项技术。它的本意是把不着急的图片推迟到快进入视口时再加载但很多实现把它变成了所有图片都推迟。第一种症状是首屏图被误伤。如果给页面上所有img都加上loadinglazy那首屏主视觉图也要等 IntersectionObserver 初始化完成、触发回调、再去发请求白白多出 100-300ms。首屏图应该是loadingeager甚至要配合fetchpriorityhigh。第二种症状是触发距离太小。默认的 rootMargin 是 0px也就是图片进入视口才开始加载用户滚到哪儿看到哪儿的空白。合理的做法是提前 300-800px 预加载让图片在进入视野前就已经在路上。第三种症状是占位高度没设。图片还没加载出来时元素高度为 0加载完成后突然撑开把下面的内容全部顶下去用户正在看的文字突然跳走。这个体验问题比慢本身更让人恼火。解决办法是用 CSS 的aspect-ratio或者给img显式写上width和height属性让浏览器提前算出占位空间。4.2 preload、fetchpriority 与 loading 的正确组合这三个属性经常被混用其实各有分工。loading控制的是浏览器原生的懒加载行为取值lazy或eager。fetchpriority控制的是同一批请求之间的相对优先级取值high、low、auto。link relpreload是让浏览器在 HTML 解析到某个标签之前就提前发起请求。首屏最大的那张图LCP 元素推荐这么写link relpreload asimage href/hero-800.webp imagesrcset/hero-400.webp 400w, /hero-800.webp 800w imagesizes100vw img src/hero-800.webp fetchpriorityhigh decodingasync width800 height450 alt...这里decodingasync很关键它让图片解码不阻塞主线程渲染避免大图解码把首屏文字渲染卡住。而对于页面底部、折叠线以下的图片简单用loadinglazy就足够了不需要额外配置。有个坑要注意不要给懒加载的图同时加 preload那等于把懒加载的收益直接抵消掉还会让浏览器在控制台给出preload 但未使用的警告。4.3 srcset 与 picture把选择权交给浏览器响应式图片的核心是让浏览器根据视口宽度和设备像素比自己挑一张最合适的下载。基础写法img srcphoto-800.jpg srcsetphoto-400.jpg 400w, photo-800.jpg 800w, photo-1200.jpg 1200w, photo-1600.jpg 1600w sizes(max-width: 600px) 100vw, 50vw width800 height600 alt...sizes告诉浏览器这张图在实际布局中会占多宽浏览器用它乘以 DPR 算出需要的像素宽度再从 srcset 里挑最接近的一档。如果 sizes 写错srcset 就完全失效——这是我见过最常见的一类错误配置很多人写了 srcset 但 sizes 保持默认的100vw结果无论什么设备都下载最大的那张。需要按格式降级时用picturepicture source typeimage/avif srcsetphoto-800.avif 800w, photo-1600.avif 1600w sizes50vw source typeimage/webp srcsetphoto-800.webp 800w, photo-1600.webp 1600w sizes50vw img srcphoto-800.jpg srcsetphoto-800.jpg 800w, photo-1600.jpg 1600w sizes50vw width800 height600 alt... loadinglazy /picture浏览器会从上往下挑第一个认识的格式都不支持才走最后的img。注意每个source都要带完整的 srcset 和 sizes不能只写一个否则可能出现格式对了但尺寸选错的情况。5. 传输链路缓存、CDN 与协议版本能省下多少5.1 缓存头配对了能省掉 90% 的重复加载图片是典型的写一次、读一万次的静态资源缓存策略应该激进到什么程度我认为带内容哈希的图片 URL 可以直接配一年Cache-Control: public, max-age31536000, immutableimmutable这个字段的作用是告诉浏览器这个 URL 对应的内容永远不会变用户在有效期内刷新页面时浏览器连条件请求都不会发直接从本地读。这比max-age31536000单独用还要快一个 RTT。要实现这一点图片 URL 里必须带内容指纹比如/img/photo-a3f9c2.webp。内容变了文件名就变天然规避了缓存不更新的问题。这套方案比短缓存 频繁校验要高效得多。对于无法加指纹的场景比如用户上传的图片URL 固定退而求其次用协商缓存Cache-Control: public, max-age3600, must-revalidate ETag: abc123这样一小时内直接用本地缓存一小时后发一个条件请求服务器返回 304 不带 body只花一个 RTT。注意 304 虽然不带内容但仍然是一次完整的网络往返在弱网下也要 200-400ms所以能用强缓存就别用协商缓存。还有一个容易被忽略的点图片响应里不要带 Cookie。图片请求带上一大坨 Cookie每个请求都会多传几十到几百字节在 HTTP/2 下还会影响头部压缩效率。做法是为图片域名单独配置或者用Cross-Origin-Resource-Policy配合凭据隔离。5.2 CDN 与图片处理服务把重活推到离用户更近的地方CDN 对图片加载的价值主要体现在两件事缩短 RTT和分担源站压力。用户请求打到就近的边缘节点RTT 从跨省的 40ms 降到同城的 5ms握手和 TTFB 都能显著改善。同时边缘节点缓存了图片源站只需要服务少量回源请求。但 CDN 的收益高度依赖缓存命中率。命中率低于 80% 的时候大部分请求还是要回源CDN 就退化成了一个慢速代理。提高命中率的关键是让图片 URL 尽可能稳定——前面提到的查询参数顺序问题、时间戳参数、无意义的随机数都是命中率杀手。图片处理服务实时裁剪、格式转换、水印要不要放在 CDN 边缘做取决于业务形态。列表页、详情页这种尺寸规格固定的场景我倾向于离线预生成上传时一次性生成所有需要的尺寸和格式存进对象存储CDN 直接缓存静态文件。这样做的好处是 TTFB 稳定在几十毫秒不依赖边缘计算节点的冷启动。代价是存储成本上升且尺寸规格变更时需要重新处理存量图片。反过来如果尺寸是动态的比如用户可拖拽的编辑器那就只能走实时处理这时候要特别注意做好处理结果的二级缓存把同样的参数组合缓存起来。5.3 HTTP/2、HTTP/3 下的图片传输差异HTTP/2 的多路复用对图片密集的页面帮助很大。HTTP/1.1 时代浏览器对同域名并发连接限制在 6 个左右页面上有 30 张图就要排队第 7 张开始必须等前面的连接释放。HTTP/2 在一条连接上跑所有请求排队问题基本消失。不过 HTTP/2 有个副作用值得注意域名分片domain sharding从优化手段变成了反优化。以前为了绕过 6 连接限制会把图片分到 img1、img2、img3 几个域名在 HTTP/2 下这样做反而会建立多条独立连接每条都要单独握手还失去了单连接的多路复用收益。所以如果你的项目还在做域名分片该把它去掉了。HTTP/3 基于 QUIC用 UDP 传输握手更快0-RTT 或 1-RTT而且在丢包场景下的表现明显优于 TCP。移动网络丢包率通常比有线高一个数量级所以在弱网场景下 HTTP/3 的收益比较可观。部署上一般 CDN 厂商会同时开启 HTTP/2 和 HTTP/3浏览器自己协商业务侧基本不用改代码。实测下来弱网环境里启用 HTTP/3 后图片下载时间能改善 10%-20%网络越差收益越明显。6. 渲染与解码那些在 Network 面板里看不到的开销6.1 解码成本与像素总量的关系Network 面板显示图片下载完了不代表用户就能看到图。浏览器还要把压缩数据解码成位图这个过程的耗时与像素总量成正比跟文件体积关系不大。一张 200KB 的 4000×3000 JPEG 和一张 200KB 的 800×600 JPEG体积一样解码耗时可能差 20 倍。每张解码后的位图占用的内存是宽 × 高 × 4字节。4000×3000 的图占 48MB800×600 只占 1.9MB。移动端浏览器给单页面的图片内存通常有几百 MB 的预算放几张 48MB 的大图就会触碰上限触发解码失败或者强制回收。表现出来可能就是图突然变成空白或者页面卡死几秒。所以控制像素总量和控制文件体积同等重要。做法就是前面说的按展示尺寸输出同时在服务端做一次兜底任何上传的图片如果长边超过某个阈值比如 3000px先整体等比缩小再存。这样即使前端漏配了尺寸也不至于出现灾难性的解码开销。用 sharp 的resize(3000, 3000, { fit: inside, withoutEnlargement: true })就能实现。6.2 尺寸未声明导致的布局抖动图片没有声明宽高时会触发一个连锁反应浏览器先按 0 高度布局图片下载完拿到尺寸后重新计算布局把下方内容整体下移。这个过程不仅让用户看到跳动还会让浏览器重新执行样式计算和布局在低端机上可能消耗几十到上百毫秒。更麻烦的是布局抖动会破坏 LCP 的测量让性能指标看起来比实际体验更飘忽。修复方式很直接在img上同时写width和height属性现代浏览器会自动用它们计算出aspect-ratio在图片加载前就预留出正确的空间。CSS 里也可以用.card-image { aspect-ratio: 16 / 9; width: 100%; height: auto; object-fit: cover; }object-fit: cover保证了即使实际图片比例和容器不完全一致也不会变形而是裁剪填充。这个组合我基本在所有图片容器上都会用。6.3 占位方案模糊图、纯色块还是骨架屏图片加载期间显示什么直接影响用户对慢的主观感受。常见的三种方案各有取舍纯色块或渐变占位成本最低没有任何额外请求缺点是视觉上比较空。适合列表页这种大量小图场景。**模糊缩略图LQIP**是把原图压到极小比如 20×15再放大并加高斯模糊作为背景图内联在 HTML 里用 base64。优点是视觉过渡自然用户能大概感知到即将出现什么内容。代价是 HTML 体积增加每张图大概多 200-500 字节的 base64。列表页有 50 张图的话就是 10-25KB需要权衡。骨架屏适合结构化的卡片布局它的重点不是暗示图片内容而是占据正确的空间并给出正在加载的预期。我自己的选择是首屏大图用 LQIP 或者主色调提取列表页用纯色占位卡片式布局用骨架屏。另外无论用哪种过渡动画都要克制transition: opacity 0.2s就够了太长的淡入会让人感觉更慢。7. 弱网与低端设备不是锦上添花而是主战场7.1 按网络质量动态降级前面所有的优化在 100Kbps 的极弱网下都会被抹平——一张 68KB 的 WebP 也要 5.4 秒。这时候唯一的出路是大幅降低质量甚至不加载。可行的降级策略有几种。一是用navigator.connection.effectiveType判断网络等级在slow-2g或2g下把图片质量档位从 2x 降到 1x甚至降到 0.5x 的极限压缩版本。二是用saveData标记判断用户是否开启了省流模式开启了就默认只加载 1x 图点击大图才加载原图。三是首屏用极小的占位图先顶上等网络空闲requestIdleCallback或者用户交互时再替换成高清版本。const conn navigator.connection; const lowEnd conn (conn.effectiveType slow-2g || conn.effectiveType 2g || conn.saveData); const srcset lowEnd ? photo-320.webp 320w, photo-640.webp 640w : photo-400.webp 400w, photo-800.webp 800w, photo-1200.webp 1200w;这些都是渐进增强的写法不支持navigator.connection的浏览器会走默认分支不会出问题。7.2 兜底机制与线上监控再好的优化也有失效的时候——CDN 节点故障、图片服务超时、用户网络突然切换。所以加载失败的兜底是必须的。img的onerror里至少要做两件事换成一张体积很小的本地占位图记录一次失败埋点。img.addEventListener(error, function handler() { this.removeEventListener(error, handler); this.src /static/placeholder.svg; reportImageFailure({ url: this.dataset.original, ts: Date.now() }); }, { once: true });埋点数据要能回答几个问题哪些图片 URL 失败率最高、失败时的网络类型分布、失败是否集中在某个时间段或某个地域。这些问题定位准了往往能发现配置层面的系统性问题而不是零散的技术细节。监控指标上我重点关注三个图片加载 P75 耗时、图片请求失败率、LCP 中图片元素的占比。前两个反映整体健康度第三个告诉你图片到底是不是首屏性能的主要瓶颈——如果 LCP 的瓶颈是字体或者 JS那把图片优化到极致也提升有限得先解决主要矛盾。还有个小细节值得提一下图片 URL 一定不要用时间戳做参数来防缓存那是给自己挖坑。正确的做法是用内容哈希既能保证更新生效又能享受最长缓存。我在实际项目里见过有人用Date.now()拼在图片 URL 后面结果每刷新一次页面所有图片全量重下CDN 命中率接近于零这种问题往往要等到线上流量成本飙升才会被发现。最后分享一个我自己常用的检查顺序遇到图慢的反馈时按这个流程走一遍基本能在半小时内定位到瓶颈先用限速禁缓存复现看 Network 里的 TTFB 和 Content Download 各占多少TTFB 高就查 CDN 缓存和回源下载时间长就查图片体积和尺寸适配两者都正常但用户还是觉得慢就去 Performance 面板看解码和布局抖动的开销。这个顺序的好处是把网络问题、资源问题和渲染问题分开处理避免在错误的层面上反复折腾。
返回列表