一个老生常谈的话题了,关于博客图片资源的优化,包括但不限于图片压缩、更优文件格式的使用以及浏览器兼容性处理。今天我打算重新整理和归纳一下。以前本站使用过基于 gulp 的图片压缩和 webp 格式转换,但由于效果不佳,转而使用了腾讯云 CDN 的数据万象服务。
现在嘛,消费降级,还是减少对付费服务的依赖吧。同时,我在刷博客时看到了 Heo 的 实现全站图片使用 avif 格式,替代臃肿的 webp 教程 ,新的图片格式真是如雨后春笋般层出不穷。Whatever,那么重新加入本地处理能力了,除了 webp,现在还要支持 avif。
一、图片压缩转换
我们有两个目标:首先是压缩原始图片,其次是将原始图片转换为 webp 和 avif 格式。考虑到 Hexo 是基于 Node.js 引擎的,所以决定使用 lovell/sharp 这个前端库。刚好,这个库不仅能压缩图片,还能转换图片格式,并且可以在 Node.js 环境中运行。不二之选,完美至极。
处理思路:
- 首先对原始图片进行压缩,生成体积小于原始文件的压缩版本。
- 仅转换特定格式的图片,通常情况下,只需处理
jpg
、jpeg
和png
等格式。 - 性能优化:如果待处理的目录中已经存在同名的 webp 或 avif 文件,就跳过该文件,不再进行处理。
补充内容:
Sharp 库支持 GIF 压缩,但会丢失动图信息,并且不支持将 GIF 转换为 webp 和 avif 格式。因此,额外添加一个判断,如果当前系统中安装了 ffmpeg,则利用它来完成 GIF 的格式转换。
ffmpeg -i ${inputFilePath} -c:v libwebp -lossless 0 -q:v 80 -loop 0 -an -vsync 0 ${outputWebPPath} |
ffmpeg -i ${inputFilePath} -c:v libsvtav1 -qp 40 ${outputAvifPath} |
完整脚本内容如下,使用时需安装对应依赖:
假设这个脚本位于 .tools/sharp.js
文件中,并且你的所有图片文件都存放在 ./source/image
目录下,你只需要在自动化部署代码中添加以下内容,即可实现自动图片转换:
node ./tools/sharps.js |
转换后的文件会保存在同一路径下,因此你的仓库体积可能会增加一点点。如果你介意空间占用,但不介意在部署时花费更多时间,那么你可以选择对 ./public/image
目录进行处理:
node ./tools/sharps.js ./public/image |
看看效果,这是三张图片:
二、图片资源调用
只关心 Chromium 系的兼容性,webp 32 支持,avif 85 支持。avif 的兼容性有那么一点点欠佳。传统技能,使用 picture 标签提供回退图像(Chromium 38)。
HTML
<picture>
元素 通过包含零或多个 source 元素和一个 img 元素来为不同的显示/设备场景提供图像版本。浏览器会选择最匹配的子<source>
元素,如果没有匹配的,就选择<img>
元素的src
属性中的 URL。然后,所选图像呈现在<img>
元素占据的空间中。
也就是对于常规 <img>
标签,替换为:
<picture> |
处理思路:
遍历所有 HTML 文件,利用 JSDOM 将其转换为 NodeList 对象,以便于后续处理。
过滤出需要处理的 img 标签,对其进行替换,若转换后的文件体积比原文件更大,则不进行替换。
对于本站,所有位于 source
目录中的图片都将通过域名 https://static.inkss.cn
进行加载。因此,如果图片的加载地址以该域名开头,就表示这是一个可以处理的图片引用。
三、图片时间戳
CDN 里的缓存时间设置得很长,如浏览器图片缓存的过期时间为七天。这导致如果图片有变动,除非强制刷新,否则二次访问时客户端是无法立即获取到最新的图片内容。
所以做如下判断:从 git 读取最近七天的提交记录,过滤出所有涉及 img 文件变动的记录。接着,类似于“图片资源调用”中的处理方法,遍历 HTML 文件中的所有 img 标签,并与前面的记录做比较。如果匹配,则在地址后追加时间戳。
git log --since="7 days ago" --name-only --pretty=format: -z |