SSG:避免动态路由页面 SSG 意外降级为 SSR

在 Next.js 项目中实现 SSG(Static Site Generation)时,使用了 generateStaticParams,动态路由页面也一定会在构建阶段生成静态页面。

如果页面中混入了依赖请求上下文的逻辑,例如错误地在 SSG 页面中使用了 @supabase/ssrcreateServerClient,那么原本应该静态生成的页面,可能会悄悄退化为动态渲染(SSR / 按需渲染),最终导致:

  • 构建时没有真正输出静态页面
  • 页面预取(prefetch)返回 404
  • SEO 和首屏性能受到影响
  • generateStaticParams 看起来写了,但实际上没有生效

1. 问题现象

最开始发现的问题是:

动态路由页面在客户端进行 prefetch 时返回了 404

例如课程页和课时页虽然使用了动态路由,但在页面跳转前预加载资源时,发现并没有命中已生成的静态页面,这通常意味着这些页面并没有在构建阶段正确输出。

后来尝试临时添加:

export const dynamic = "force-static";

发现页面行为开始接近预期。

这说明问题并不在 generateStaticParams 本身,而是页面中的某些逻辑影响了渲染模式,导致原本应该走 SSG 的页面,被 Next.js 判定成了动态页面。


2. 根本原因

继续回溯后发现,问题出在页面的数据查询客户端上。

这是因为 createServerClient 的设计初衷是为了处理基于 Cookie 的身份验证(SSR 场景),它依赖于 Next.js 的 cookies() 函数。但在 SSG 模式下(即执行 next build 时),这些函数不可用。

对于 SSG 页面,应该直接使用原生 @supabase/supabase-js 提供的 createClient。这个客户端不依赖于请求上下文(Cookies),非常适合在构建阶段获取公共数据。

操作: 创建 /lib/supabase/static.ts 之后在SSG页面中使用静态客户端


3. 验证是否生效

  1. 运行 pnpm build 时观察构建输出

    ● /[courseSlug]
    │ ├ /raytonx-learn-from-zero
    │ └ /supabase-fundamentals
    ├ ● /[courseSlug]/lessons/[lessonSlug]
    │ ├ /supabase-fundamentals/lessons/why-supabase
    │ ├ /raytonx-learn-from-zero/lessons/raytonx-learn-start-up
    │ ├ /raytonx-learn-from-zero/lessons/email-auth-nextjs-supabase
    │ └ [+6 more paths]
    
  2. 部署后,观察 prefetech 是否正常