跳到主内容
Zhimalab

Astro 6 → 7 升级实战:Rust 编译器 + Rolldown 迁移踩坑记录

2026-08-04 · 1 分钟

本站(zhimalab)之前一直在 Astro 6.4.8 上运行。随着 Astro 7 稳定发布——它带来了 Rust 编译器(@astrojs/compiler 重写)和内置 Vite 8(打包器从 Rollup 换成 Rolldown)——我把整个站点实测升级了一遍。本文记录这次升级的完整过程、踩过的坑和真实收益。

为什么值得升级

Astro 7 的两大变化对静态站非常友好:

  1. Rust 编译器:模板编译从 Go/JS 时代彻底切换到 Rust,页面渲染和构建速度大幅提升。
  2. 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 rollupOptionsrolldownOptions

environments: {
  client: {
    build: {
      // 旧:rollupOptions: { output: { manualChunks(id) { ... } } }
      rolldownOptions: {
        output: { /* ... */ }
      }
    }
  }
}

2.2 manualChunkscodeSplitting.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/blog 5/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+ 条错误是既有问题:主要是 webkitAudioContextexecCommand 等纯 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 的空白规则。这三关过了,剩下的基本都是体力活。