next.js SSG

2025-2-04 02:55:055

#nextjs

5

介绍

  • SSG,英文全称“Static Site Generation”,中文翻译“静态站点生成”。
  • SSG 会在构建阶段,就将页面编译为多个静态的 HTML 文件。就是根据动态数据输出成固定的 html 文件,兼顾了文件缓存以及 seo 的需求。
  • 比如打开一篇博客文章页面,既然所有人看到的内容都是一样的,没有必要在用户请求页面的时候,服务端再请求接口。干脆先获取数据,提前编译成 HTML 文件,等用户访问的时候,直接返回 HTML 文件。这样速度会更快。再配上 CDN 缓存,速度就更快了。 example

项目配置

next.config.ts文件

import type { NextConfig } from "next";
 
const nextConfig: NextConfig = {
  /* config options here */
  output: 'export',
};

export default nextConfig;

  • 将构建模式调整为'export',即可让nextjs项目跑build指令时,输出静态html文件,接着便可以上传到对应的服务器上来进行访问。

应用

静态路由

无需特别操作,按照正常的写法,将一个页面分为服务端区域(纯静态)与客户端区域

服务端组件

export default async function Page() {
  // This fetch will run on the server during `next build`
  const res = await fetch('https://api.example.com/...')
  const data = await res.json()
 
  return <main>
      ...
     </main>
}

服务端组件不允许使用state和effect,也就是说不能进行状态管理,这也意味着只能写一些简单的静态页面,当然,针对一些静态官网项目,这是很好用的

客户端组件

'use client'
 
export default function Page() {
  
  const [data,setData] = useState()
  useEffect(()=>{
     const data = await fetch('https://api.example.com/...')
     setData(data)
  })
 
  return <div>
      {data.name}
  </div>
}

开发和传统SPA应用没有区别,其实nextjs也可以开发SPA应用,只要将所有组件定义为客户端组件即可

总的来说,按照上述两个写法,能满足大多数简单项目的开发了 如,要求SEO的部分,尽量使用服务端组件来生成静态html内容 强交互部分,如各类小工具,则使用客户端组件来生成动态部分

动态路由

// app/blog/[slug]/page.js
// 获取一个总的slug列表
export async function generateStaticParams() {
  const posts = await fetch("https://.../posts").then((res) => res.json());

  return posts.map((post) => ({
    slug: post.slug,
  }));
}

// Multiple versions of this page will be statically generated
// using the `params` returned by `generateStaticParams`
export default function Page({ params }) {
  const { slug } = params;
  const res = await fetch(`https://api.example.com/${slug}`)
  const data = await res.json()
  // ...
}

其中 [slug] 代表 slug 这个必填,下面的 generateStaticParams 函数作用就是把所有 slug 通过数组的形式进行返回出来,之后自行选择客户端组件或是服务端组件即可 在构建阶段,nextjs会根据posts数组,输出/blog/ 下数个页面

动态meta

import type { Metadata, ResolvingMetadata } from 'next'
 
type Props = {
  params: Promise<{ id: string }>
  searchParams: Promise<{ [key: string]: string | string[] | undefined }>
}
 
export async function generateMetadata(
  { params, searchParams }: Props,
  parent: ResolvingMetadata
): Promise<Metadata> {
  // read route params
  const id = (await params).id
 
  // fetch data
  const product = await fetch(`https://.../${id}`).then((res) => res.json())
 
  // optionally access and extend (rather than replace) parent metadata
  const previousImages = (await parent).openGraph?.images || []
 
  return {
    title: product.title,
    openGraph: {
      images: ['/some-specific-page-image.jpg', ...previousImages],
    },
  }
}
 
export default function Page({ params, searchParams }: Props) {}

静态数据api

文件:app/data.json/route.ts

export async function GET() {  
    return Response.json({ name: 'Lee' })
}

ssg不支持的功能

  • 不能确定数据的动态路由
  • 重定向
  • 动态api路由
  • 中间件等等服务端高级特性