833 字
4 分钟

Astro Fuwari 博客 Vercel 构建 OOM 优化实战

问题背景#

我的博客使用 Astro 构建,部署在 Vercel 上。随着文章数量增长到 500+ 篇,遇到了构建失败的问题:

ELIFECYCLE Command failed with exit code 137.
Error: Command "pnpm run build" exited with 137

image-20251210100502720 51be99fdd7a7e930407980363f7f7037

Exit code 137 意味着进程被 SIGKILL 终止,通常是因为内存不足 (OOM)。Vercel 免费版提供 8GB RAM,理论上应该足够,但实际构建时内存峰值过高。

问题分析#

通过分析构建日志和代码,我发现了几个内存瓶颈:

1. getStaticPaths 传递完整 entry 对象#

这是最关键的问题。原来的代码:

export async function getStaticPaths() {
const posts = await getSortedPosts();
return posts.map((entry) => ({
params: { slug: entry.id },
props: { entry }, // 传递完整 entry 对象!
}));
}

这意味着 500 篇文章的完整内容都被存储在路由 props 中,同时占用内存。

2. 默认无限制并发构建#

Astro 默认会尽可能多地并行构建页面,在内存受限环境下会导致峰值过高。

3. HTML 压缩消耗额外内存#

虽然 HTML 压缩可以减少文件大小,但在构建时需要额外内存。而 CDN(如 Cloudflare、Vercel)会自动使用 Brotli 压缩传输,本地压缩意义不大。

优化方案#

1. 核心优化:getStaticPaths 只传递必要数据#

export async function getStaticPaths() {
const posts = await getSortedPosts();
// 只传递必要的导航数据
return posts.map((entry) => ({
params: { slug: entry.id },
props: {
slug: entry.id,
prevSlug: entry.data.prevSlug,
prevTitle: entry.data.prevTitle,
nextSlug: entry.data.nextSlug,
nextTitle: entry.data.nextTitle,
},
}));
}
// 在渲染时按需获取完整内容
const { slug, prevSlug, prevTitle, nextSlug, nextTitle } = Astro.props;
const entry = await getEntry("posts", slug);
const { Content } = await render(entry);

这个优化将 getStaticPaths 阶段的内存占用减少了 90% 以上

2. 增加 Node.js 内存限制#

{
"scripts": {
"build": "NODE_OPTIONS='--max-old-space-size=7168' astro build"
}
}

Node.js 默认堆内存限制约 2-4GB,显式设置为 7GB 可以充分利用 Vercel 的 8GB RAM。

3. 限制构建并发#

astro.config.mjs
export default defineConfig({
build: {
concurrency: 2, // 限制并发构建
},
});

有趣的是,测试发现 concurrency: 2 比无限制更快:

  • concurrency: 2:2m 19s ✅
  • 无限制:3m 45s ❌

过多并发会导致资源竞争,反而变慢。

测试方法:

  • 首先concurrency: 2
  • 运行构建并计时
  • 先增加到4,再增加到6,最后增加到8
  • 使用最快设置
  • 重要提示:并非越多越好!案例研究发现,在12核系统中,4个CPU是最佳选择。

image-20251210100709880

4. 禁用 HTML 压缩#

astro.config.mjs
export default defineConfig({
compressHTML: false, // 让 CDN 处理压缩
});

CDN 使用 Brotli 压缩(比 gzip 更高效),本地 HTML 压缩的收益被覆盖,不如省下这部分内存和时间。

5. 升级构建目标#

// astro.config.mjs - vite 配置
vite: {
build: {
target: 'es2022', // 减少代码转译
},
}

6. 修复 Expressive Code 重复主题#

// 修复前(bug)
themes: [theme, theme], // 重复了
// 修复后
themes: [theme],

优化效果#

指标优化前优化后
构建结果OOM 失败成功 ✅
构建时间-2m 19s
内存占用超过 8GB正常范围

总结#

对于大规模 Astro 博客的构建优化,最重要的是:

  1. getStaticPaths 只传递必要数据 - 这是核心优化
  2. 合理的并发限制 - 不是越多越好
  3. 利用 CDN 压缩 - 本地压缩可以省略
  4. 充分利用可用内存 - 设置 NODE_OPTIONS

这些优化不仅解决了 OOM 问题,还让构建更快更稳定。

参考#

Astro Fuwari 博客 Vercel 构建 OOM 优化实战
https://catcat.blog/2025/12/astro-vercel-oom-optimization.html
作者
猫猫博客
发布于
2025-12-10
许可协议
CC BY-NC-SA 4.0