跳转到内容
Bruce's Service
返回

把博客从 Next.js 迁移到 Astro:那些没人提前警告你的坑

过去几周我把这个博客从 Next.js 搬到了 Astro。理由老生常谈——更少的 JS、更可控的标记、只在需要的地方上岛屿架构。但没人告诉你的是,换了运行时之后会因为一些”运行时特有”的 bug 白白搭进去三天。

1. Satori 会悄无声息地拒绝你的字体

OG 图生成本地测试一切正常,构建后却生成空白图片。原来 Satori——大多数 astro-og-canvas 类方案背后依赖的库——不支持 woff2,只认 ttfotfwoff。它不报错、不警告,只是默默用回退字体渲染文字然后继续跑。

// 静默失败
const font = await fetch("/fonts/GoogleSansCode.woff2").then(r => r.arrayBuffer());

// 正常工作
const font = await fetch("/fonts/GoogleSansCode.ttf").then(r => r.arrayBuffer());

如果你的 OG 图看起来像是 Times New Roman 顶的班,先检查字体格式。

2. 服务端没有 getPointAtLength

我有一个签名组件,用 fontkit 描边路径,靠 getPointAtLength 计算描边动画的位置。浏览器里完美运行,构建直接报错。

Astro 默认服务端渲染组件——没有 DOM,自然也没有 SVGPathElement,更别提 getPointAtLength 了。解法是把这类调用挡在 client 指令后面,或者干脆用 fontkit 在构建时算好点位,而不是依赖浏览器 API:

---
// 构建时计算路径点,而非渲染时
import { getPathPoints } from "../lib/signature";
const points = getPathPoints(text);
---
<svg>{points.map(p => <circle cx={p.x} cy={p.y} r="1" />)}</svg>

任何假设”存在活的 DOM”的代码,在 SSR/SSG 场景下都是危险信号——这是从纯客户端 React 代码库带过来的习惯,很容易不知不觉就踩进去。

3. React 的写法不能照搬——这才是重点

真正的迁移工作大部分是体力活,但有意思的决策恰恰藏在这些”体力活”里:

ReactAstro
children<slot />
clsxclass:list
lucide-react@lucide/astro
useSWRfrontmatter 里的 fetch

这些替换单独看都不难,但每一个都逼你回答一个小小的架构问题:这东西真的需要交互吗?我一半所谓的”组件”其实是套着 React 外壳的静态标记。Astro 会把这一点摆到明处,因为默认就是零 JS,想要交互得自己主动加回来。


结果是:更小的包体积、更少的活动部件,外加几个 bug,教会我的 SSR 知识比三年 App Router 加起来还多。如果你正在迁移路上碰到”就是不报错但也不对”的情况,先看看是不是依赖了浏览器 API,或者某个已经不成立的构建期假设——我这三个坑,根源都在这。