博客新家:基于 astro.js

2017 年我的博客从 WordPress 迁移到 Github Pages,开启了 Git Markdown 和静态渲染之路,一开始是 Ruby 和 Jekyll,2023 年迁移到 Next.js,使用现代 JavaScript 框架和生态提供的组件化开发、页面渲染和函数调用能力,它由 Vercel 驱动,使用阿里云 DCDN 提供静态加速。

这套系统工作起来没什么问题,但开始出现一些坏味道:我在逐步完善代码的时候,越来越感到没有动力:比如博客的 Tag 标记和索引页支持,比如博客大纲、回到顶部、AI 标识等功能。这当然不意味着 Next.js 做不到这些,但模糊的客户端和服务端渲染边界、复杂的页面接口调用、独特行为的路由跳转和图片动态缓存机制,越来越消磨着我的开发动力。

那么就维持现状吧。好也不好,Vercel “独家”部署总是让我心里隐隐不安,我曾经尝试过腾讯 Edge Page 服务、Cloudflare 的 Page 服务等多种供应商,大家的“基本支持”也都 OK,但总有一些奇奇怪怪的小毛病,我开始意识到,Next.js 对于我的使用场景过于复杂了。

压死骆驼的倒数第二根稻草是 Node 18 生命周期结束,Vercel 强制要求构建必须声明使用更高级的 Node 版本。这件事情说大不大,但搜索引擎提供了多种处理办法,我试了好几种,包括且不限于在页面更改 Node 版本,在项目的 vercel.json 声明版本,在 astro 的配置文件中声明版本,无一例外都失败了,最后扒文档才知道要在 package.json 中声明 engine。构建一次次失败的报错只是显示着 Edge Function 不能使用配置的 22 版 Node,因此降级为 Node 18,但没有任何具体原因表明为什么降级到 Node 18。搜索引擎的混乱结果是 Vercel 对 Next.js 开发路线的不稳定(app router 和 page router)和配置文件不稳定的恶果交织而成的,最终转嫁到使用者身上。

一次偶然的机会,我有一个使用 AI 重构博客的计划,在一个平常普通的夜晚,在 Gemini 2.5 Pro 和 Sonnet 4 的努力下,经历过几次提交和完善,我完整复刻了 Next.js 的组件、样式,基本对齐了之前版本的特性。Astro.js 的 SSG 不再使用托管的服务商,而是我直接在 Github Action 构建后,将静态网页推送到阿里云 OSS 上,直接作为静态页面提供。从这个角度说,这是一次架构的简化,之前 CodeBase -> Gitea -> Gitlab -> Vercel -> Aliyun DCDN 到现在的 CodeBase -> Gitea -> Github Action -> Aliyun OSS,完美契合我的使用需求。我之前使用 OSS 提供 a.com 的静态文件和子站点服务,a.com 默认跳转到 www.a.com 以访问网站,这对搜索引擎而言并不友好,而现在静态文件、子站和 Astro.js 使用一个 OSS Bucket,整体更清晰明了。

Astro.js 的 Island 架构很棒,因此我将各种功能的补充完善逐步提上日程,我相信它们用 AI 做起来也得心应手。更重要的是,这套革新摆脱了多个在当下可能不稳定的供应商依赖,去掉了阿里云 DCDN 和 Vercel 源站通信的时间和金钱成本,让整个服务更加可靠和健壮。

此外,在 CI/CD 过程中,我尝试了多个将 Github 构建上传到 OSS 的开源 Action,但要么要干掉整个 Bucket,要么直接不覆盖历史文件,各种毛病应有尽有,我不得已使用 Go 花了 3 分钟开发了一个,基于 Aliyun OSS Go SDK,5 分钟测试验证完毕,支持 20 协程的并发写入和覆盖支持。在 AI 时代,我断言软件工程中"变化的边界"将会继续收缩,从之前的 XML、JSON 配置到已经流行 10 年的代码配置,并且到现在的 SDK 和 API。

总的来说,“技术为需求服务”是第一宗旨,也是唯一要务。这是“实事求是”的朴素例证,也是 AI 时代开发者迫切需要转变的思路:与其在复杂的抽象层中寻找解决方案,不如利用 AI 的能力,直接在最根本的服务层构建最适合自己的、最精简的解决方案