Next.js 静态化改造:放弃 Astro 的理由与实践
天下武功,唯快不破!最近优化博客搞得有点魔怔了。原本的计划是用 Astro 彻底重构整个项目,毕竟 Astro 在静态站点生成方面的优势实在太明显了。但在深入了解之后,我发现 Next.js 其实也能实现几乎相同的效果,而且迁移成本更低。本文就来分享一下这次 Next.js 静态化改造的完整过程。
为什么想要重构?
其实原因很简单:性能和成本。目前的博客使用 Next.js 的 SSG(静态站点生成)模式,但仍然依赖 Next.js 的服务端运行时。这意味着:
- 部署时需要一个 Node.js 服务器(虽然 Vercel 免费提供)
- 每次访问都有一定的运行时开销
- 托管成本虽然不高,但不是真正的零成本
而 Astro 是纯静态站点生成器,输出的是纯粹的 HTML/CSS/JS,可以部署到任何免费的静态托管服务上,真正的零成本。这让我心动了很久。
Next.js 静态化的可能性
在准备切换到 Astro 之前,我仔细研究了 Next.js 的静态导出功能。结果 Next.js 完全可以实现和 Astro 几乎相同的静态化效果。
对比分析
| 特性 | Astro | Next.js 静态导出 |
|---|---|---|
| 静态 HTML 输出 | ✅ 默认 | ✅ 支持(output: 'export') |
| 零运行时成本 | ✅ 纯静态 | ✅ 纯静态 |
| 免费托管 | ✅ 任何静态服务 | ✅ 任何静态服务 |
| React 生态 | ❌ 需要集成 | ✅ 原生支持 |
| 迁移成本 | ❌ 需要重写大部分代码 | ✅ 只需修改配置 |
| 学习曲线 | ❌ 新框架、新语法 | ✅ 无需学习 |
| 现有组件复用 | ❌ 需要改写 | ✅ 100% 复用 |
看到这些对比,还切换什么Astro?Next.jsy一样挺香啊。
最关键的是,性能差异几乎可以忽略不计。静态导出后的 Next.js 博客,Lighthouse 分数可以轻松达到 95+,和 Astro 几乎没有区别。
实施过程
1. 修改 Next.js 配置
首先,在 next.config.mjs 中添加静态导出配置:
export default {
output: 'export', // 启用静态导出
trailingSlash: true, // 添加尾随斜杠
images: {
unoptimized: true, // 禁用图片优化(静态导出必需)
},
// ... 其他配置保持不变
}
2. 禁用 ISR
在所有使用 getStaticProps 的页面中,添加 revalidate: false:
export async function getStaticProps() {
// ... 数据获取逻辑
return {
props: { /* 数据 */ },
revalidate: false, // 禁用 ISR,纯静态生成
};
}
3. 配置 App Router 路由
如果使用了 App Router(如 RSS Feed、Sitemap 等),需要添加:
export const dynamic = 'force-static';
4. 优化搜索功能
搜索功能遇到了一个有趣的问题。原本使用动态路由 /search/[query],但静态导出后无法预生成所有可能的搜索词路由。
解决方案是将搜索功能改为纯客户端实现:
// 在 /search 页面中
export async function getStaticProps() {
const allPosts = getAllPosts(); // 获取所有文章数据
return {
props: { allPosts }, // 传递给客户端
revalidate: false,
};
}
// 客户端实时搜索
const [searchQuery, setSearchQuery] = useState('');
const [filteredPosts, setFilteredPosts] = useState([]);
useEffect(() => {
if (searchQuery.trim()) {
const filtered = allPosts.filter(post =>
post.title?.toLowerCase().includes(searchQuery.toLowerCase())
);
setFilteredPosts(filtered);
}
}, [searchQuery, allPosts]);
这样既解决了静态导出的问题,又提供了更好的用户体验(实时搜索)。
5. 添加本地预览脚本
为了方便测试,在 package.json 中添加了预览脚本:
{
"scripts": {
"preview": "python -m http.server 8000 --directory out"
}
}
构建验证
完成配置后,运行构建命令:
npm run build
构建成功后,会看到类似这样的输出:
Route (pages)
┌ ● / (920 ms)
├ ● /blog/[id] (374947 ms)
│ ├ /blog/20260206132113 (1578 ms)
│ └ [+365 more paths]
├ ● /tags/[tag] (123863 ms)
│ ├ /tags/react (1128 ms)
│ └ [+148 more paths]
└ ● /search (920 ms)
构建产物会输出到 out 目录,包含所有生成的静态文件。
部署方式
现在部署方式完全和 Astro 一样了:
本地测试
npm run build # 构建
npm run preview # 预览(访问 http://localhost:8000)
部署到静态托管服务
可以选择任何免费的静态托管服务:
- Cloudflare Pages(推荐):全球 CDN、免费 SSL
- GitHub Pages:完全免费,GitHub 原生支持
- Netlify:免费额度大,部署简单
- Vercel:仍然可以使用,但现在是纯静态部署
最棒的是,如果你之前已经部署到 Vercel,部署流程完全不需要改变!只需要 git push,Vercel 会自动识别静态导出配置并正确部署。
性能对比
之前(SSG 模式)
- 构建时间:~30 秒
- 部署方式:需要 Next.js 运行时
- 冷启动:可能存在延迟
- 运行时成本:免费但有配额限制
现在(静态导出)
- 构建时间:~11 秒
- 部署方式:纯静态文件
- 冷启动:立即响应
- 运行时成本:真正的零成本
Lighthouse 性能
静态导出后的性能表现:
- Performance: 95+
- Accessibility: 100
- Best Practices: 100
- SEO: 100
和 Astro 几乎没有区别!
经验总结
这次静态化改造让我学到了很多:
- 不要盲目追求新技术:Astro 很棒,但不是唯一选择
- 渐进式优化优于重构:小步快跑,逐步改进
- 了解现有工具的潜力:Next.js 的功能远比我想象的强大
- 成本效益分析很重要:考虑迁移成本,而不仅仅是技术优劣
最终决定
最终我选择了 Next.js 静态化,而不是 Astro。原因很简单:
- ✅ 零迁移成本
- ✅ 100% 代码复用
- ✅ 相同的性能表现
- ✅ 更低的维护成本
- ✅ 团队熟悉的技术栈
Astro 依然是一个优秀的框架,但对于已经使用 Next.js 的项目来说,静态导出可能是更务实的选择。
下一步
静态化只是第一步,接下来还可以考虑:
- 进一步优化图片(手动压缩、转换为 WebP/AVIF)
- 添加 CDN 加速
- 实现更高级的缓存策略
- 优化构建产物大小
但目前的性能已经非常好了,这些可以作为后续的优化方向。
参考资源
总结:有时候,最好的解决方案不是追逐最新的技术,而是充分利用现有工具的潜力。Next.js 静态导出让我在不改变技术栈的情况下,获得了和 Astro 几乎相同的性能优势,这是最完美的选择。
本文为原创文章,遵循: CC BY-NC-SA 4.0版权协议。
本文链接:https://www.suiyan.cc/blog/20260208005127