图像加载策略设计 - 掌握 preload、fetchpriority 和 decoding

· 9 分钟阅读

图像加载的性能瓶颈与优化方向

图像加载涉及发现、请求、下载、解码四个阶段。每个阶段都有优化空间。

加载阶段:

  • 发现:浏览器解析 HTML 发现 img 标签。预加载扫描器可提前发现
  • 请求:发起 HTTP 请求。受并发连接数和优先级影响
  • 下载:传输图像数据。受带宽和文件大小影响
  • 解码:将压缩数据解码为位图。CPU 密集,可能阻塞主线程

LCP 图像的关键路径:LCP 元素通常是主视觉图。优化其加载路径可直接改善 LCP 指标。目标:让 LCP 图像尽早被发现、高优先级请求、快速下载、异步解码。

Chrome 的 5 级资源优先级:Chrome 内部以 Highest、High、Medium、Low、Lowest 5 个等级管理资源优先级。默认情况下视口内的图像被归为 High、视口外的图像被归为 Low;但这一判定要等布局计算完成后才进行,因此在初期阶段全部图像都被当作 Medium 处理,最优的优先级排布被推迟。另外 <picture> 元素内的图像要在媒体查询求值之后才能确定下载目标,被发现的时机比单纯的 <img> 更晚。

开发者可用的 3 个属性:针对上述问题,开发者可显式指示优先级的手段是 preloadfetchprioritydecoding 3 个属性。它们分别在不同层面优化图像加载,组合使用时存在把 LCP 改善 500ms-1 秒以上的案例。

preload - 提前发现关键图像

<link rel="preload"> 告诉浏览器尽早开始加载指定资源,即使 HTML 解析器尚未发现该资源。

用法:<link rel="preload" as="image" href="hero.webp">

适用场景:

  • CSS 背景图:浏览器需要下载并解析 CSS 后才能发现背景图。preload 可提前数百毫秒开始加载
  • JavaScript 动态插入的图像:JS 执行后才创建的 img 元素,preload 可提前加载
  • LCP 图像:确保 LCP 图像以最高优先级尽早加载

响应式 preload:

  • <link rel="preload" as="image" imagesrcset="small.jpg 400w, large.jpg 800w" imagesizes="100vw">
  • 浏览器根据视口选择合适的尺寸预加载

注意:过度使用 preload 会与其他资源竞争带宽。仅对 LCP 图像和关键 CSS 背景图使用。

preload 特别有效的 3 种场合:<link rel="preload"> 写在 HTML 的 <head> 中,可让资源在 DOM 解析的最初期就开始取得。其一是 CSS 的 background-image 指定的图像——通常要等「CSS 文件下载 → 解析 → 构建 CSSOM → 构建渲染树」这一连串过程完成后,浏览器才认识到该图像的存在。其二是 SPA (Single Page Application) 的主视觉图,或轮播的第一张图像。其三是用 <picture> 做条件分支的图像。

实现例与滥用的警告:基本写法为 <link rel="preload" as="image" href="/images/hero.avif" type="image/avif" fetchpriority="high">;响应式图像的 preload 要用 imagesrcsetimagesizes 属性,写作 <link rel="preload" as="image" imagesrcset="hero-400.avif 400w, hero-800.avif 800w, hero-1200.avif 1200w" imagesizes="100vw" type="image/avif">。preload 很强力,但滥用会反效果:若 Chrome DevTools 的 Console 出现「preload 的资源在 3 秒内未被使用」的警告,就应删除不必要的 preload。

fetchpriority - 精细控制加载优先级

fetchpriority 属性让开发者明确告诉浏览器资源的重要程度,影响请求的调度顺序。

取值:

  • high:高优先级。用于 LCP 图像、首屏关键图像
  • low:低优先级。用于首屏外的图像、装饰性图像
  • auto:浏览器自行判断(默认)

用法:<img src="hero.jpg" fetchpriority="high">

与 loading 属性的配合:

  • LCP 图像:loading="eager" fetchpriority="high"(不懒加载 + 高优先级)
  • 首屏装饰图:loading="eager" fetchpriority="low"(不懒加载但低优先级)
  • 首屏外图像:loading="lazy"(懒加载,fetchpriority 无意义)

效果:在带宽有限时,high 优先级的图像会先于 low 优先级的图像开始下载。可将 LCP 改善 100-400ms。

取值与内部效果:fetchpriority 的取值为 highlowauto (默认) 3 个。fetchpriority="high" 的作用是把 Chrome 的内部优先级从 Medium 提升到 High。

实现例与适用范围:写作 <img src="hero.jpg" fetchpriority="high" alt="主视觉图" width="1200" height="600">,同时指定 widthheight 也能防止 CLS。fetchpriority 可用于 <img><link rel="preload"><script><iframe>

decoding 属性 - 控制图像解码时机

decoding 属性控制图像解码是否阻塞主线程渲染。

取值:

  • async:异步解码,不阻塞页面渲染。图像可能在渲染后才显示
  • sync:同步解码,确保图像在下一帧渲染前解码完成。可能阻塞渲染
  • auto:浏览器自行决定(默认)

推荐策略:

  • 大多数图像:decoding="async"。避免大图解码阻塞页面渲染
  • LCP 图像:不设置或 decoding="auto"。让浏览器优化 LCP 的解码时机
  • 动态插入的图像:decoding="async"。避免 JavaScript 插入图像时阻塞主线程

createImageBitmap 的关系:对于需要在 Canvas 中使用的图像,createImageBitmap 可在 Web Worker 中解码,完全不阻塞主线程。

取值与效果的界限:decoding 的取值为 syncasyncauto (默认) 3 个。指定 async 时,图像解码期间 DOM 的构建与脚本的执行仍可继续,整页渲染因而加快;对 2,000 px 以上的大图,或多张图像同时解码的场面,效果尤为明显。反之,阻塞主线程会恶化 INP (Interaction to Next Paint),因此 sync 要谨慎使用。

综合加载策略设计

将各种加载优化技术组合为完整的策略,针对不同类型的图像应用不同的加载方式。

分层策略:

  • LCP 图像(1 张):preload + fetchpriority="high" + 不懒加载 + 响应式 srcset
  • 首屏其他图像(2-5 张):不懒加载 + fetchpriority="auto" + srcset
  • 首屏外图像:loading="lazy" + 占位(aspect-ratio 或 LQIP)
  • 装饰性图像:loading="lazy" + fetchpriority="low"

HTTP 头优化:

  • 103 Early Hints:在 HTML 响应前提示浏览器预加载 LCP 图像
  • Link: <hero.webp>; rel=preload; as=image:HTTP 头中的 preload

4 种典型模式:模式 1 (CSS 背景图的 LCP 优化)——在 <head> 写入 <link rel="preload" as="image" href="hero-bg.avif" type="image/avif" fetchpriority="high">,并对相应元素设置 background-image。模式 2 (首屏的 img 元素)——写作 <img src="hero.jpg" fetchpriority="high" decoding="async" loading="eager" width="1200" height="600" alt="...">,用 fetchpriority 提升优先级、用 decoding="async" 防止阻塞主线程、用 loading="eager" (默认) 关闭懒加载。模式 3 (视口外的图像)——写作 <img src="below-fold.jpg" loading="lazy" fetchpriority="low" decoding="async" width="800" height="400" alt="...">,把加载推迟到进入视口,并以 low 避免与其他图像争抢带宽。模式 4 (轮播的第一张图像)——只对第一张幻灯片设 fetchpriority="high" + loading="eager",第 2 张以后设 fetchpriority="low" + loading="lazy"

实测效果:组合运用上述模式后,某电商网站的商品列表页 LCP 从 3.2 秒改善到 1.8 秒 (缩短 44%)。

性能监控与验证

实施加载策略后需要验证效果,持续监控性能指标。

验证工具:

  • Chrome DevTools Network:查看图像的请求时序、优先级(Priority 列)和大小
  • Lighthouse:检测未优化的图像(未压缩、未正确尺寸、缺少懒加载)
  • WebPageTest:瀑布图可视化加载顺序,确认 LCP 图像是否最先加载

关键指标:

  • LCP:最大内容绘制时间。LCP 图像的加载策略直接影响此指标
  • CLS:累积布局偏移。懒加载图像的占位是否正确
  • Total Blocking Time:图像解码是否阻塞了主线程

RUM(真实用户监控):

  • 使用 PerformanceObserver 监控实际用户的 LCP 元素和时间
  • 按设备类型、网络条件分析图像加载性能
  • 识别特定图像导致的性能问题(如过大的未优化图像)

常见错误 3 例:错误 1 — 对 LCP 图像使用 loading="lazy":它会把下载推迟到图像接近视口,用在 LCP 图像上会造成 300-500ms 的延迟;应在 Chrome DevTools 的 Performance 面板中确定 LCP 元素,确认该图像没有加上 lazy。错误 2 — 过度使用 preload:preload 3 张以上的图像会引起带宽争抢,反而拖慢整体;要确认 Console 是否出现 The resource was preloaded using link preload but not used within a few seconds 的警告。错误 3 — 滥用 fetchpriority="high":对全部图像都设 high,优先级的区分就失去了意义。

验证与计测工具:在 Waterfall 图表中可视地确认各图像开始与完成下载的时刻,验证 LCP 图像是否最先下载完成;Lighthouse 的「Largest Contentful Paint element」一节可确认 LCP 元素与改善建议;WebPageTest 的 filmstrip view 能以 100ms 为单位追踪实际的渲染过程。RUM 方面用 web-vitals 库收集数据,比较引入 fetchpriority 前后的 LCP 分布——若 p75 值改善 200ms 以上,即可判定该措施成功。

相关文章

图像懒加载实现指南 - loading=lazy 与 IntersectionObserver 的选择

系统讲解图像懒加载的实现方法。对比原生 loading=lazy 和 IntersectionObserver 方案,涵盖性能优化和最佳实践。

Web 图像性能审计 - Core Web Vitals 改善实践指南

Web 图像性能审计的完整方法论。涵盖审计工具与指标、LCP 优化、CLS 防止、传输大小优化及持续监控体系。

Core Web Vitals 与图像优化 - 改善 LCP、CLS 和 INP 的实用方法

图像优化对 Core Web Vitals 的影响及改善方法。涵盖 LCP 加速策略、CLS 防止、INP 优化及懒加载最佳实践。

Web 图像优化清单 - 15 项生产环境可执行项目

面向生产环境的系统化图像优化清单,涵盖格式选择、压缩策略、响应式图片、加载策略和分发缓存等 15 个关键项目。

图像 SEO 完全指南 - 通过 Alt 文本、文件名和尺寸优化提升搜索流量

全面解析图像 SEO 优化策略,涵盖 alt 属性写法、文件命名、结构化数据、Core Web Vitals 优化和图像站点地图。

响应式图像实现指南 - srcset、sizes 与 picture 元素完全指南

响应式图像的完整实现指南。涵盖 srcset 属性、sizes 属性、picture 元素的艺术指导及构建流程中的自动化生成。

相关术语