
这套方案是我在博客图片处理上的一次完整实践总结。无论用 Astro、Hexo、Hugo 还是其他静态站点生成器,都能参考这里的思路,优化图片性能。从压缩、上传、云端处理到前端适配,全链路自动化,省心高效。
NOTE写这篇文章的一些思考可以在 Github Issue - AVIF 格式引入与浏览器兼容性探讨 #3 查看
效果展示
先来看一下整体流程:
背景
图片质量和加载速度一直是博客体验的关键。尤其面向国内用户,既要保证清晰度,又要让页面秒开,还得控制流量和成本。经过多次探索,逐步形成了这套低成本、高兼容的图片处理方案。
核心需求
- 性能优先:图片体积尽量小,加载速度尽量快
- 兼容性强:各种浏览器都能正常显示
- 自动化流程:写作到发布全自动,无需手动处理
- 成本可控:优先选用免费或低价服务
技术选型
- 图片上传:PicGo
- 图片存储:Cloudflare R2
- 图片处理:Cloudflare Images API
- 自动化:Cloudflare Worker
- 图片分发:主要依赖 WebP Cloud Services,结合 R2 存储
- 前端适配:多框架通用方案
适用场景
方案适用于 Astro、Hexo、Hugo、WordPress 及其他静态站点,核心思路通用,前端实现可灵活调整。
架构设计
1. 图片上传层
PicGo 配合 Cloudflare R2,搭建免费图床。具体细节可参考:从零开始搭建你的免费图床系统 (Cloudflare R2 + WebP Cloud + PicGo)
2. 云端处理层
我编写了一个 Worker 来进行图像处理。访问 Worker URL 或调用 API 时,会自动生成 AVIF、WebP、JPG 等多种格式,无需人工干预。支持健康检查和队列,保证自动化和可靠性。
主要流程:
- Worker 检查图片扩展名,决定是否处理
- 立即返回处理状态,异步执行图片任务
- 从 R2 拉原图,上传到 Cloudflare Images,生成多格式后再存回 R2
相关代码和部署细节可以在 GitHub 仓库 查看,有兴趣可以参考源码和实现思路。
3. 前端适配层
不同博客框架下,图片适配方式略有不同,但核心思路一致。
核心实现代码
前端图片适配的实现,Astro 下可以通过配置 Sharp 服务自动生成多种图片格式。比如在 astro.config.mjs
里:
export default defineConfig({ image: { service: { entrypoint: "astro/assets/services/sharp", config: { formats: ["avif", "webp", "jpeg"], quality: { avif: 80, webp: 80, jpeg: 85, png: 85, }, progressive: true, }, }, },});
如果需要更智能的图片处理和降级,可以用自定义组件。比如 ImageWrapper.astro
,会自动遍历格式优先级,生成 <source>
标签,并根据图片来源动态降级。部分核心逻辑如下:
---// ...参数处理与导入省略...
async function generateImageFormats(image: ImageMetadata) { const formats = [ { name: "avif", type: "image/avif" }, { name: "webp", type: "image/webp" }, { name: "jpeg", type: "image/jpeg" }, { name: "png", type: "image/png" }, ]; const availableFormats = []; for (const format of formats) { try { const optimizedImage = await getImage({ src: image, format: format.name as any, width: image.width, height: image.height, }); availableFormats.push({ src: optimizedImage.src, format: format.name, }); } catch (error) { // ...错误处理省略... } } // ...降级逻辑与返回省略...}---// ...渲染部分省略...
完整组件代码和详细逻辑可以参考:ImageWrapper.astro 源码。
对于 Hexo、Hugo 等静态博客,直接用 <picture>
标签即可实现多格式适配:
<picture> <source srcset="https://your-r2-domain/images/photo.avif" type="image/avif"> <source srcset="https://your-r2-domain/images/photo.webp" type="image/webp"> <img src="https://your-r2-domain/images/photo.jpg" alt="Photo description" loading="lazy"></picture>
或用 JS 自动化批量处理图片标签:
function optimizeImages() { const images = document.querySelectorAll('img[data-optimize]'); images.forEach(img => { const picture = document.createElement('picture'); picture.innerHTML = ` <source srcset="${img.src.replace(/\.\w+$/, '.avif')}" type="image/avif"> <source srcset="${img.src.replace(/\.\w+$/, '.webp')}" type="image/webp"> ${img.outerHTML} `; img.parentNode.replaceChild(picture, img); });}
TIP建议结合自身需求和技术栈,探索适合自己的图片优化方案。可以参考
<picture>
标签的多格式适配思路,也可以尝试插件、自动化脚本或第三方服务。每个框架都有不同的扩展方式,灵活调整才能获得最佳效果。
兼容性与探索
AVIF 格式虽然高效,但并非所有浏览器都支持。实际测试发现,部分老旧浏览器甚至新版本也有兼容性问题。采用 <picture>
标签,优先提供 AVIF,其次 WebP,最后 JPEG 或 PNG,浏览器会自动选择支持的格式,兼容性和体验兼顾。
也尝试过 avif.js、WebAssembly 等方案,但维护和兼容性不理想。最终还是觉得 <picture>
标签最简单可靠。图片优化方面,Sharp 虽然强大,但图片放仓库会导致仓库膨胀,影响构建和拉取效率。现在远程图片都用 webp.se 或 Cloudflare R2 分发,省心高效。
性能优化策略
渐进式加载
所有图片都用 lazy loading 和异步解码:
<Image loading="lazy" decoding="async" // ... 其他属性/>
格式优先级
现代浏览器支持度排序:
- AVIF(最优)
- WebP
- JPEG/PNG(兜底)