为什么图片成了静态站的心病
博客迁移到 Hugo 之后,我一度觉得一切都很完美:构建快、部署简单、不需要服务器。直到某天我用手机打开自己的站点,发现首页加载了半分钟,那张十几兆的风景照还在转圈。那一刻我意识到,静态网站省下的服务器开销,迟早要在图片上传上还回去。
问题其实很朴素:我们拍照、截图、从设计稿导出素材,顺手拖进 `static/images`,根本没人在意一张 4000px 宽、12MB 的照片对读者意味着什么。静态站又没有服务端可以动态裁剪和压缩,所有原始图片都会原封不动地推给浏览器。几篇文章下来,页面体积直奔几十兆,再快的 CDN 也救不回来。
三条路:格式、尺寸、工具链
图片压缩这件事无非三个维度:选对格式、控制尺寸、自动化处理。
格式选择
过去我们习惯用 JPEG 和 PNG,分别处理照片和透明图。现在有了更优解:
- 01WebP
压缩率明显优于 JPEG,尤其在照片类内容上,同等画质体积能降一半以上。所有主流浏览器早就支
- 02AVIF
压缩率比 WebP 还激进,但编码耗时更长,不是所有场景都划算。
- 03SVG
图标、Logo、示意图直接用 SVG,它是矢量格式,跟分辨率无关,体积通常只有几个 KB。
我的做法是:截图存 PNG 或 WebP,照片转 WebP,图标一律 SVG。
尺寸控制
这是最容易被忽略的一步。博客正文区宽度通常只有 700~900px,你上传一张几千像素宽的图片,浏览器即使自动缩放了,传输的字节数一点没少。所以在压缩之前,先把图片缩放到实际展示尺寸的两倍(应对高分屏),再考虑压缩格式,效果立竿见影。
—
实操:我的 Hugo 图片流水线
整个流程无非三步:上传前先跑一遍脚本,把图片变成“适合网页的样子”;构建时交给 Hugo 自带的图片处理能力做最终优化;最后在部署前检查一遍产出体积。
本地批处理脚本
我用的是 Node 脚本,配合 `sharp` 库。`sharp` 基于 libvips,处理速度很快,适合批量操作。核心逻辑就几件事:
“`javascript
const sharp = require("sharp");
const fs = require("fs");
const path = require("path");
const inputDir = "./原始图片";
const outputDir = "./static/images";
fs.readdirSync(inputDir).forEach((file) => {
const ext = path.extname(file).toLowerCase();
if (![".jpg", ".jpeg", ".png", ".webp"].includes(ext)) return;
sharp(path.join(inputDir, file))
.resize({ width: 1600, withoutEnlargement: true })
.webp({ quality: 75 })
.toFile(path.join(outputDir, path.parse(file).name + ".webp"));
});
“`
这个脚本把图片统一缩放到最长边 1600px(兼顾文章配图和社交分享卡片),转成 WebP,质量设 75。对大多数内容来说,这个参数能在体积和观感之间取得很好的平衡。
利用 Hugo 自身的处理能力
Hugo 内置了图片处理功能,从 `resources` 引用图片时可以用 `resize`、`fit` 等方法动态生成不同尺寸的版本。比如文章封面图,我会在模板里生成一张 WebP 缩略图,用于列表页:
“`
{{ $image := .Resources.GetMatch "cover.*" }}
{{ $thumb := $image.Resize "800x webp q75" }}
<img src="{{ $thumb.RelPermalink }}" alt="{{ .Title }}">
“`
这样做的好处是,处理逻辑跟着内容走,不需要维护一堆中间文件。
部署前自查
最后一道保险:在 CI 流程里加一个体积检查,超过阈值的图片直接抛错提醒。我用的是一个简单的 shell 命令,扫出超过 300KB 的文件:
“`bash
find static -name "*.webp" -size +300k -exec ls -lh {} ;
“`
有提示,有动力去优化。几次下来,新文章配图基本都能保持在 200KB 以内。
可能踩进去的坑
WebP 的问题偶尔也会冒出来,最常见的几个:
- 老旧的图片查看器打不开。Windows 资源管理器早些版本预览不了 WebP,需要注意。解决方法是备份原始图片,别只留压缩版。
- 截图的细节保持。文字截图转 WebP 有时会产生轻微模糊或色偏,这类图我建议保留 PNG。
- 缩略图和原图脱节。如果文章封面和正文用的不是同一张图,记得生成缩略图前先确认焦点,否则很容易截掉人脸。
从源头开始省
图片压缩不是一个一次性动作,而是应该嵌入到日常写作流程里的习惯。现在每次往博客里塞图之前,我都会先跑一遍脚本,再预览一下压缩后的画质。这样下来,整个站点的体积控制得很轻松,迁移到任何静态托管商都不用操心流量和加载速度。
流程跑通之后,图片处理就不再是负担。静下来想想,这大概就是技术博客该有的样子:工具为内容服务,但工具本身值得被认真对待。