跳到主要内容
NKDShinKu
技术5 分钟阅读

配图用什么格式?我最后留下了 SVG 和 WebP

PNG、JPG、GIF、WebP、AVIF、SVG 各自适合什么场景,为什么这个博客的配图统一走「源用 SVG、发行用 WebP」,以及这套流程怎么跑起来。

配图用什么格式?我最后留下了 SVG 和 WebP

做配图工具链的时候我发现,真正要回答的不是「哪个格式最好」,而是「一张图的哪个环节,该用哪种格式」。

现在的答案很简单:源文件用 SVG,发行文件用 WebP。这篇先把常见格式的区别说清楚,再说这套流程怎么跑。

先分清两个环节#

  • 源文件:你要改、要 diff、要重新生成的那一份。它决定「改起来贵不贵」。
  • 发行文件:读者浏览器真正下载的那一份。它决定「打开快不快」。

两个目标不一样,最佳格式也就不一样。混在一起讨论,很容易变成「WebP 和 PNG 到底哪个好」这种没有答案的问题。

配图的两个环节:源文件(SVG,由生成模块产出)经渲染压缩成为发行文件(WebP),再上传图床、被文章引用

常见格式差在哪#

格式 压缩方式 透明 动画 适合 不适合
PNG 无损(DEFLATE) 支持 不支持(APNG 另算) 截图、UI、线条图 照片,体积会失控
JPEG 有损(DCT) 不支持 不支持 照片、连续色调 文字截图,边缘会振铃、发虚
GIF 无损,但上限 256 色 支持(1 位) 支持 简单表情图 渐变、照片,色带明显且体积大
WebP 有损 + 无损 支持 支持 网页发行 需要极老环境兼容时
AVIF 有损 + 无损 支持 支持 体积敏感的场景 回退链更长
SVG 矢量,无分辨率概念 支持 支持(CSS / SMIL) 图标、示意图、图表、封面 照片、复杂纹理

几个容易混的点:

  • JPEG 没有透明通道,需要透明就只能换 PNG 或 WebP;
  • GIF 的「无损」不等于画质好,它把颜色压到 256 种,渐变必然出色带;
  • SVG 不是位图格式,是图形描述,它的清晰来自矢量,和位图的「清晰」不是一回事。

Google 官方给过一组对比:WebP 无损比 PNG 小 26%,有损在同等 SSIM 质量下比 JPEG 小 25%–34%,无损透明图约是 PNG 的三分之一(来源:developers.google.com/speed/webp)。

拿自己的图跑一遍#

光看结论没感觉,我用手上两张真实图片测了一轮,工具就是项目里的 sharp:

格式 文字截图 1506×451(原 PNG 114KB) 彩色示意图 1601×708(原 PNG 472KB)
WebP 无损 77.5KB(68%,像素完全一致) 161KB(34%,像素完全一致)
WebP q90 38.0KB(33%) 34.5KB(7%)
JPEG q90 45.3KB(40%) 55.5KB(12%)
AVIF q50 20.4KB(18%) 15.7KB(3%)
GIF 43.8KB(38%) 134KB(28%)

两个结论:

  1. 截图和示意图上,WebP 全面压过 PNG 和 JPEG:无损就能省掉三到六成体积,q90 直接省到只剩 7%–33%。GIF 对纯色截图还凑合,一到彩色渐变就是 WebP 的三四倍。
  2. AVIF 体积更小,但我们没用。同一张示意图上,AVIF q50 编码 77ms、WebP q90 编码 70ms——耗时差不多,体积只有一半。不用的原因不是体积也不是速度,而是:多一种格式就多一条回退链,而我们的图本来只有几十 KB,省下的那 20KB 不值得多维护一层。

为什么源文件用 SVG#

  • 它是文本:能 diff、能 review、能参数化。我们每张示意图都是一个模块(scripts/figures/*.mjs),一帧就是一个状态,改图等于改代码。
  • 矢量的好处是确定的:2 倍输出不糊,坐标和字号都是数字,不靠「看着差不多」。
  • 颜色跟站点同源:颜色直接从设计 token 里取,不会出现「图里还是上一版的蓝」。
  • 还有现实原因:博客需要的配图大多是结构、流程、对比这类图形,恰好是 SVG 的强项。

为什么发行文件用 WebP#

  • 体积:上面那张表就是理由。
  • 动画:动图用动画 WebP 代替 GIF。GIF 只有 256 色,渐变必有色带;同样的内容在彩色图上,GIF 通常是 WebP 的三四倍。
  • 无损也支持透明:想要像素级不变,无损 WebP 就够,不用退回 PNG。
  • 支持面:现代浏览器都支持,为兼容性保留 PNG 回退已经没多大必要。

我们是怎么做的#

三类图,三条路径:

图类型 谁决定 用什么 怎么产出
说明性图(结构、流程、状态) AI 判断,有必要才加 代码生成的 SVG → WebP pnpm figures <名称>
证据性图(截图、报错、数据) 个人提供 截图 / 照片 → WebP pnpm images <文件>
封面 脚本 SVG → WebP 960×600 pnpm generate:covers

说明性图的流程:

  1. 在 scripts/figures/<名称>.mjs 里写一个模块,按帧参数化生成 SVG(静态图就是一帧);
  2. 跑 pnpm figures <名称>:sharp 渲染成 2 倍图,多帧自动合成动画 WebP(需要 .gif 时加 --format gif);
  3. 生成器会校验尺寸一致、画面非空白、多帧之间确有差异,并打印体积。

证据性图的流程:

pnpm images shot.png --lossless     # 无损 WebP,像素完全一致
pnpm images shot.png --quality 90   # 有损,体积更小

脚本默认不缩放,只有显式传 --scale / --width 才改尺寸,并且会告警:把截图弄糊的主因是缩小尺寸和反复转码,不是有损压缩。它还会打印 PSNR,方便判断这一档能不能接受。

最后两步:图片上传到 R2(正文图放在 images/posts/<slug>/),文章里用绝对 URL 引用、alt 必填;流程图和时序图优先用 Mermaid,它跟暗色主题联动,改起来也只是改几行文本。

没做的事#

  • 不用 AI 画插图:我们的配图要么在「说明」、要么在「证明」,画图模型出来的东西改不动,也难和站点保持同一套配色。
  • 不输出 AVIF:观察中,理由上面写了。
  • 没有文字越界检测,也没有网页截图工具——示意图的排版目前靠人工核对。

小结#

源文件要能改,发行文件要够小。这两个目标本来就不同,所以格式也不该是同一个。

(实测数据由项目脚本跑出,参数:sharp 0.35.4,WebP effort: 5、JPEG mozjpeg、AVIF effort: 3。)