1399 字
7 分钟

文章配图流水线:截图、压成 WebP、随构建分发

写技术文章一半时间花在图上。这套流水线只有四条规则:截图必须真实、按出现顺序编号、统一压成 WebP、和文章目录同名进仓库。附实测压缩数据(2073KB → 357KB)与五个踩过的坑。
10月24日 19:00

写一篇带实测的技术文章,大概一半时间花在图上:截哪几张、截图里的数字对不对、压完还看不看得清、放进哪个目录。这些事重复十几次之后,必须有流水线——否则每次都要重新想一遍。

我这 13 篇文章一共 51 张配图、3.63 MB,全部走同一条流水线。规则只有四条。

规则一:截图必须是真实页面的#

不做示意图、不用拼贴。写性能就打开 Chrome 的 Performance 面板或跑真实脚本,写 Cloudflare 就截控制台里的真实配置。文章里说「实测」的每一个数字,都要能在截图里找到。

截图工具是 Playwright + 本机 Chrome,必须 --headed —— 无头模式会被 Cloudflare 的浏览器校验拦住,截出来是一片空白。常用参数就几个:

# 整页截图,等 3 秒让懒加载与动画稳定,顺手隐藏 Cookie 横幅
node shot.mjs --url https://blog.peroe.cn/posts/xxx/ \
  --out ./02-post.png --width 1280 --height 900 --scroll 700 \
  --hide "#termsfeed-com---nb" --wait 3500

--hide 那一条是血泪:Cookie 横幅固定在底部,不藏掉会挡住正文的结论部分。

规则二:文件名就是出场顺序#

配图放在 public/assets/images/posts/<slug>/,目录名与文章 slug 完全一致,文件按正文出现顺序编号:

public/assets/images/posts/blog-image-pipeline/
├── 01-convert.webp     ← 正文里第一张出现的图
├── 02-post.webp
└── 03-weight.webp

正文里用绝对路径引用(以 / 开头,走 public 目录):

![压缩实测:2073KB → 357KB](/assets/images/posts/blog-image-pipeline/01-convert.webp)

目录名和 slug 不一致是最常见的 404 来源:文章能构建成功、图片全裂,而且本地开发时因为缓存还可能看着正常。

规则三:统一压成 WebP,宽度封顶#

截图先存 PNG(无损、方便复核),再统一转 WebP:

压缩实测:一批截图 2073KB → 357KB

我这批的压缩率是 83%,单张从几百 KB 降到一个屏都不一定用得完的大小。脚本做的事情很简单:Pillow 打开、convert("RGB")、save(quality=80)、限制最大宽度 1500px(截图放大就看不清字了,1500 够用)。

几个经验值:

类型建议原因
终端输出 / 表格宽度 1000~1100、quality 80等宽字体在高压缩下容易糊
网页截图宽度 1280~1400、quality 80再大就是浪费,页面本身按 1200 设计
图表 / 细线quality 85细线在 WebP 里最先出现振铃
单张上限300 KB超过就该考虑裁掉无关区域

规则四:图进仓库,不走图床#

这是被讨论最多的一条。我选择把图片直接提交进仓库,理由是三个「不」:

  1. 不会跑图:图床跑路、防盗链、限速、跨域——这些问题全部消失,图片和 HTML 同源、同一套 CDN;
  2. 不依赖第三方:访客打开文章时,除了统计与评论,不再有任何外部图片请求;
  3. 构建时能校验:图片不存在,构建产物里就是一个 404 的 <img>,本地一看就知道。

代价是仓库体积。所以我给单张设了 300 KB 的上限(当前最大的一张 184 KB),并且只保留文章里真正用到的图——草稿截图、重复截图一律不进仓库。

目前 13 篇文章共 51 张图、3.63 MB,按文章分布

3.63 MB 对一个博客来说完全可以接受:它随构建分发,不占额外存储、不需要额外域名,也不会因为某个第三方服务挂掉而集体失效。

五个踩过的坑#

1. 忘了按顺序编号。写完后插入了一张新图,编号乱了,正文里的 01/02/03 与目录对不上。解决办法是写完先列一遍图的位置,再统一编号。

2. 直接把 PNG 提交了。一张 1280×900 的网页截图 PNG 有 800 KB 到 1.2 MB,同样尺寸的 WebP 只有 40~120 KB。现在 public/ 里不允许出现 PNG 截图。

3. 目录名与 slug 不一致。改 slug 时忘了改目录,图片全 404。现在改 slug 会顺手 git mv 目录。

4. 截图里带着指纹信息。浏览器窗口标题栏、书签栏、系统时钟都可能泄露「这台机器是谁」。要么截图前隐藏,要么裁掉页头。

5. 正文里的数字与截图对不上。有一次脚本重跑后数字变了,正文还写着旧值。现在改完会重新截图,并把数字直接从脚本输出里抄——不要凭记忆写数字。

一条命令的收尾流程#

现在的固定动作是这样:

# 1. 截图(headed,藏掉 Cookie 横幅)
node shot.mjs --url <文章地址> --out 02-post.png --width 1280 --hide "#termsfeed-com---nb"

# 2. 转 WebP(顺便限制宽度)
python to-webp.py . --quality 80 --max-width 1500

# 3. 确认体积(单张 < 300KB)
ls -lh *.webp

# 4. 写文章、引用、构建
pnpm astro build --force

四步走完,图的格式、体积、路径、命名就都是确定的——写正文时可以专心在内容上。

小结#

配图流水线的价值不在「省时间」,而在让每次的结果一样:同样的命名规则、同样的压缩参数、同样的引用路径。当这些都不需要再想的时候,写文章才真正只剩下「把话说清楚」这件事。

三条可以立刻照抄的规则:真实截图(数字能对上)、顺序编号 + 目录同名(不会 404)、一律 WebP 且单张 < 300 KB(不拖慢文章页)。其余的(工具、参数)按自己的习惯换都行。

格式选择(继续用 WebP 还是上 AVIF)的实测数据在图片格式的收益边界。

这篇文章是否对你有帮助?

发现错误或想要改进这篇文章?

在 GitHub 上编辑此页

文章修订历史 (1 次)

查看变更记录
10月24日 23:18:07 c20be15

docs(baseline): 第 30 轮终检——数字更新到最终状态,补第 28~30 轮小结

文章配图流水线:截图、压成 WebP、随构建分发
作者
peroe
发布于
10月24日 19:00
许可协议
CC BY-NC-SA 4.0

相关文章