本站(zhimalab)之前一直在 Astro 6.4.8 上运行。随着 Astro 7 稳定发布——它带来了 Rust 编译器(@astrojs/compiler 重写)和内置 Vite 8(打包器从 Rollup 换成 Rolldown)——我把整个站点实测升级了一遍。本文记录这次升级的完整过程、踩过的坑和真实收益。
为什么值得升级
Astro 7 的两大变化对静态站非常友好:
- Rust 编译器:模板编译从 Go/JS 时代彻底切换到 Rust,页面渲染和构建速度大幅提升。
- Vite 8 + Rolldown:依赖预构建和打包全部走 Rolldown,构建链路更快,chunk 输出更可控。
对本站这种 327 个页面的纯静态站来说,最大的直接收益就是构建时间。
升级清单
只动了三个依赖,其余(React 19、three、Tailwind 等)全部保持不变:
| 依赖 | 旧版本 | 新版本 |
|---|---|---|
astro |
^6.4.8 |
^7.1.6 |
@astrojs/react |
^5.0.7 |
^6.0.2 |
@astrojs/mdx |
^5.0.6 |
^7.0.5 |
@astrojs/sitemap |
^3.7.3 |
不变 |
pnpm install
pnpm astro build
坑 1:Rust 编译器严格校验 HTML 闭合
第一次构建直接失败,报错:
[astro] error Closing tag has no matching opening tag
定位到 src/pages/tutorial/geography-map.astro 第 82 行有一个多余的 </div>。这在 Astro 6 的宽松编译器下能被容忍,但 Rust 编译器会严格校验标签配对。删除后构建即通过——全仓库 1000+ 个文件里只有这一处坏 HTML,这算是运气不错。
坑 2:Vite 8 / Rolldown 配置迁移
这是本次升级工作量最大的部分。本站 astro.config.mjs 里有精细的手动分包(React、three、Phaser、MediaPipe 各自独立 chunk),在 Rolldown 下有 3 处必须改:
2.1 rollupOptions → rolldownOptions
environments: {
client: {
build: {
// 旧:rollupOptions: { output: { manualChunks(id) { ... } } }
rolldownOptions: {
output: { /* ... */ }
}
}
}
}
2.2 manualChunks → codeSplitting.groups(重点)
output.manualChunks 在 Rolldown 中已弃用,改为 output.codeSplitting.groups。最大的坑在这里:Rolldown 的分组是递归捕获依赖的,而且分组之间靠 priority 决定谁先捕获。如果不设优先级,@react-three 组会先把 react、three 全部吞进自己组里,最终 react 核心 + three.js + 整个 R3F 生态合并成一个 1.3MB 的巨型 chunk,所有页面被迫加载。
正确写法是给 React/three 相关分组显式设置优先级:
codeSplitting: {
groups: [
{ test: /node_modules[\\/](react|react-dom|scheduler)[\\/]/, name: 'react-vendor', priority: 30 },
{ test: /node_modules[\\/]three[\\/]/, name: 'three-core', priority: 20 },
{ test: /node_modules[\\/]@react-three[\\/]/, name: 'react-three', priority: 10 },
{ test: /node_modules[\\/]@mediapipe[\\/]/, name: 'mediapipe' },
{ test: /node_modules[\\/]phaser[\\/]/, name: 'phaser' },
{ test: /node_modules[\\/]html2canvas[\\/]/, name: 'html2canvas' },
{ test: /node_modules[\\/]prismjs[\\/]/, name: 'prism' },
{ test: /node_modules[\\/]lucide-react[\\/]/, name: 'lucide' },
],
}
两个小细节:
- 正则里用
[\\/]兼容 Windows 路径分隔符(官方推荐)。 - react 组要写成
(react|react-dom|scheduler)[\\/],避免把@react-three里的 react 误匹配进 react-vendor。
迁移后实测 chunk 拆分与 Astro 6 完全一致:react-vendor 186KB、three-core 694KB、react-three 451KB、phaser 1343KB、html2canvas 195KB、mediapipe 133KB。普通工具页只加载 react-vendor + rolldown-runtime,没有被大 chunk 拖累。
2.3 optimizeDeps 的 define 位置变了
旧配置里用 optimizeDeps.esbuildOptions.define 固定 React 开发模式的 NODE_ENV,在 Vite 8 下直接报错:
Invalid key: Expected never but received define
Rolldown 中 define 被移到 transform 级:
optimizeDeps: {
rolldownOptions: {
transform: {
define: { 'process.env.NODE_ENV': '"development"' },
},
},
}
坑 3:JSX 空白规则带来的可见回归
这是最隐蔽的坑。Astro 7 的 compressHTML 默认值从「HTML 语义压缩」改成了 'jsx'(JSX 规则):
- 同一行内的空白会保留;
- 跨行、换行处的空白会被移除。
对本站这种中文内容站来说,影响是可见的。构建产物里直接复现了 404 页的文案粘连:
<!-- 源模板 -->
<span>🫘</span>
芝麻开门 —— 但这次门后面什么也没有
<!-- Astro 7 默认 'jsx' 模式产物 -->
<span>🫘</span>芝麻开门 —— 但这次门后面什么也没有
全仓库扫描下来约 70 个文件存在「跨行内联元素 + 文本」的写法(首页的 ⚡ 快捷体验传送门、诗仙游戏里的 💡 小锦囊 等约 50 处有真实可见风险)。逐个手动加 {" "} 不现实,官方给出的恢复方案是在 astro.config.mjs 里显式设置:
export default defineConfig({
compressHTML: true, // 恢复 Astro 6 的 HTML 语义压缩
})
改完后重新构建,空白恢复正常,327 页一次通过。如果升级后不做任何处理,这部分会悄悄变成视觉回归。
收益:构建提速 2.7 倍
| 阶段 | Astro 6.4 | Astro 7.1 | 提升 |
|---|---|---|---|
| 构建核心 | 1m 21s | 29s | ~2.8x |
| 页面整体(327 页) | 1m 31s | 37s | ~2.5x |
实际跑下来整体 1m31s → 37s,提速约 2.7 倍。对 CI 部署来说,这个提升非常可观。
验证
升级后做了以下验证,全部通过:
pnpm astro build:327 页构建成功,sitemap 正常生成。- Dev server:抽查 5 个页面全部 200,日志干净。
- 生产 preview + Playwright 冒烟:
/、/tools/sums-out、/ai、/tutorial/geography-map、/blog5/5 通过,React island 正常 hydration,无控制台错误。 pnpm generate:index:搜索索引正常生成(103 条文档)。
遗留风险与既有问题
升级后有几个点需要留意,其中部分与升级无关:
- 浏览器 target 提升:Vite 8 / Rolldown 输出目标默认升到 Chrome 111+ / Firefox 114+ / Safari 16.4+。现代浏览器无碍,如需兼容旧浏览器要另配 target。
- CJS interop:Rolldown 对 CommonJS 模块的 interop 规则与 Rollup 有差异,个别老包可能行为不同。本项目构建 + 冒烟未见问题。
astro check的 7000+ 条错误是既有问题:主要是webkitAudioContext、execCommand等纯 JS DOM 类型错误,Astro 6 下同样存在,与升级无关。- robots.txt 一直缺失:仓库里本来就没有
public/robots.txt,Playwright 用例里对它的断言会 404,这也是既有问题,顺手补一个即可。
小结
Astro 6 → 7 升级对本站是低风险、高收益的:
- 代码改动极小:3 个依赖版本 + 1 处坏 HTML 修复 +
astro.config.mjs迁移; - 构建提速约 2.7 倍;
- 主要成本在于升级后的视觉回归——尤其是
compressHTML空白规则变化,建议升级后用compressHTML: true保持既有渲染行为,再跑一轮全站截图对比。
如果你也在考虑升级,重点检查三件事:Rust 编译器的标签闭合校验、Rolldown 手动分组的 priority、compressHTML 的空白规则。这三关过了,剩下的基本都是体力活。