常见报错:payloadcms [Error: Failed to collect page data

生产部署的常见难题:数据库连接

在使用Payload CMS与Next.js结合构建网站时,尤其是在容器化环境如Docker中进行生产部署,一个常见的“拦路虎”就是构建阶段对数据库连接的需求。这通常会让开发者感到困惑,因为他们可能认为构建阶段只需要编译代码,而不需要与数据库进行实际交互。

谁才是真正的“罪魁祸首”?

这里需要明确一点:Payload本身在构建时并不强制要求数据库连接。它是一个灵活的CMS,其本地API设计允许在运行时按需获取数据。然而,当Payload与Next.js的静态站点生成(SSG)功能结合时,情况就变得复杂了。
问题出在Next.js的SSG机制。如果你的任何路由段(route segments)启用了SSG(这通常是默认行为,除非你明确选择退出或使用了动态API)并且这些路由段在构建时通过Payload的本地API来获取数据,那么Next.js在尝试预渲染这些静态页面时,就需要一个活跃的数据库连接来查询数据。在没有数据库连接的情况下运行pnpm next build,很可能会导致构建失败。
那么,面对这个痛点,我们有哪些解决方案呢?

解决方案一:利用Next.js的experimental-build-mode

Next.js提供了一个实验性的构建模式,允许你分阶段进行构建,从而巧妙地绕开构建初期对数据库连接的依赖。

1. 仅编译代码,无需数据库

你可以使用以下命令来只编译Next.js应用程序的代码,而不进行静态页面的生成:
BASH
pnpm next build --experimental-build-mode compile
执行此命令时,Next.js将仅专注于代码编译,不会尝试预渲染静态页面,因此也不会要求数据库连接。在这种模式下,你的所有页面将在运行时动态渲染。这对于CI/CD管道中需要快速编译并部署基础代码的步骤非常有用。

2. 生成静态页面(需要数据库连接)

当你拥有数据库连接后(例如,在部署到生产环境后,数据库服务已启动并可访问),你可以运行以下命令来生成静态页面:
BASH
pnpm next build --experimental-build-mode generate
此命令将基于之前编译的代码,结合数据库中的数据,生成所有配置为SSG的静态页面。这是你最终获得高性能、预渲染网站的关键步骤。

关于环境变量的注意事项

使用pnpm next build --experimental-build-mode compile时,需要特别注意环境变量的处理:
  • NEXT_PUBLIC_前缀的环境变量:在compile阶段,这些以NEXT_PUBLIC_为前缀的环境变量不会被内联到客户端代码中,因此在客户端环境中它们将是undefined
为了解决这个问题,你有两种选择:
  • 当有数据库连接时:运行pnpm next build --experimental-build-mode generate。此命令不仅会生成静态页面,还会正确处理NEXT_PUBLIC_环境变量的内联。
  • 当你仍然没有数据库连接,但需要NEXT_PUBLIC_环境变量可用时:可以使用pnpm next build --experimental-build-mode generate-env。这个命令将专注于处理环境变量的内联,而不尝试生成完整的静态页面。
更多详细信息请参考Next.js官方文档

解决方案二:全面禁用SSG(Opt-out of SSG)

如果你不希望或不需要SSG带来的性能优势,或者只是想简化构建流程,可以考虑完全禁用SSG。这意味着你的所有页面都将在请求时动态渲染。

如何禁用SSG?

在每个你需要禁用SSG的路由段文件中,添加以下导出配置:
TYPESCRIPT
export const dynamic = 'force-dynamic';
这将强制Next.js为该路由段及其所有子路由动态渲染页面,从而在构建时无需数据库连接。在部署到Vercel等平台时,此设置也等同于SSR(服务器端渲染)。

权衡与取舍

请注意,禁用SSG将禁用静态优化。这意味着你的网站可能因为每次请求都需要服务器渲染而变得更慢,尤其是在高流量场景下。因此,在选择此方案时,务必权衡其对性能和用户体验的影响。
更多关于dynamic选项的细节,请查阅Next.js官方文档

总结与建议

在Payload和Next.js的生产部署中,解决构建阶段数据库连接的痛点至关重要。你拥有的两种主要策略各有优劣:
  1. 分阶段构建(experimental-build-mode
    • 优点:允许你在没有数据库连接的情况下进行初步编译,然后在数据库可用时再生成静态页面,结合了动态渲染和SSG的优势。
    • 缺点:增加了构建流程的复杂性,需要管理多个构建命令。
    • 适用场景:对性能有较高要求,希望充分利用SSG优势,但CI/CD环境对数据库可用性有限制。
  2. 禁用SSG(export const dynamic = 'force-dynamic'
    • 优点:简化构建流程,完全移除构建阶段对数据库的依赖。
    • 缺点:牺牲了静态优化的性能优势,网站加载可能会变慢。
    • 适用场景:对实时数据有较高要求,或网站规模较小对性能敏感度较低,希望最简化部署流程。
选择哪种方法取决于你的具体项目需求、性能目标以及部署环境的特点。理解这些解决方案,将帮助你更顺畅地将Payload + Next.js应用程序推向生产环境,尤其是在Docker这样的容器化部署场景中。