Menu

generateStaticParams

generateStaticParams 函数可以与 动态路由段 结合使用,以在构建时 静态生成 路由,而不是在请求时按需生成。

app/blog/[slug]/page.js
// 返回一个 `params` 列表来填充 [slug] 动态段
export async function generateStaticParams() {
  const posts = await fetch("https://.../posts").then((res) => res.json());
 
  return posts.map((post) => ({
    slug: post.slug,
  }));
}
 
// 使用 `generateStaticParams` 返回的 `params` 
// 静态生成此页面的多个版本
export default function Page({ params }) {
  const { slug } = params;
  // ...
}

值得注意的是

  • 你可以使用 dynamicParams 段配置选项来控制访问未由 generateStaticParams 生成的动态段时会发生什么。
  • 你必须 generateStaticParams 返回一个空数组 或使用 export const dynamic = 'force-static',以便在运行时重新验证(ISR)路径
  • next dev 期间,当你导航到一个路由时,会调用 generateStaticParams
  • next build 期间,generateStaticParams 在生成相应的布局或页面之前运行。
  • 在重新验证(ISR)期间,不会再次调用 generateStaticParams
  • generateStaticParams 替代了 Pages Router 中的 getStaticPaths 函数。

参数

options.params (可选)

如果路由中的多个动态段使用 generateStaticParams,则对于父级生成的每组 params,子级 generateStaticParams 函数会执行一次。

params 对象包含父级 generateStaticParams 填充的 params,可用于 在子段中生成 params

返回值

generateStaticParams 应返回一个对象数组,每个对象表示单个路由的已填充动态段。

  • 对象中的每个属性都是要填充的路由的动态段。
  • 属性名是段的名称,属性值是该段应填充的内容。
示例路由generateStaticParams 返回类型
/product/[id]{ id: string }[]
/products/[category]/[product]{ category: string, product: string }[]
/products/[...slug]{ slug: string[] }[]

单个动态段

app/product/[id]/page.tsx
export function generateStaticParams() {
  return [{ id: "1" }, { id: "2" }, { id: "3" }];
}
 
// 使用 `generateStaticParams` 返回的 `params`
// 静态生成此页面的三个版本
// - /product/1
// - /product/2
// - /product/3
export default function Page({ params }: { params: { id: string } }) {
  const { id } = params;
  // ...
}
app/product/[id]/page.js
export function generateStaticParams() {
  return [{ id: "1" }, { id: "2" }, { id: "3" }];
}
 
// 使用 `generateStaticParams` 返回的 `params`
// 静态生成此页面的三个版本
// - /product/1
// - /product/2
// - /product/3
export default function Page({ params }) {
  const { id } = params;
  // ...
}

多个动态段

app/products/[category]/[product]/page.tsx
export function generateStaticParams() {
  return [
    { category: "a", product: "1" },
    { category: "b", product: "2" },
    { category: "c", product: "3" },
  ];
}
 
// 使用 `generateStaticParams` 返回的 `params`
// 静态生成此页面的三个版本
// - /products/a/1
// - /products/b/2
// - /products/c/3
export default function Page({
  params,
}: {
  params: { category: string; product: string };
}) {
  const { category, product } = params;
  // ...
}
app/products/[category]/[product]/page.js
export function generateStaticParams() {
  return [
    { category: "a", product: "1" },
    { category: "b", product: "2" },
    { category: "c", product: "3" },
  ];
}
 
// 使用 `generateStaticParams` 返回的 `params`
// 静态生成此页面的三个版本
// - /products/a/1
// - /products/b/2
// - /products/c/3
export default function Page({ params }) {
  const { category, product } = params;
  // ...
}

捕获所有动态段

app/product/[...slug]/page.tsx
export function generateStaticParams() {
  return [{ slug: ["a", "1"] }, { slug: ["b", "2"] }, { slug: ["c", "3"] }];
}
 
// 使用 `generateStaticParams` 返回的 `params`
// 静态生成此页面的三个版本
// - /product/a/1
// - /product/b/2
// - /product/c/3
export default function Page({ params }: { params: { slug: string[] } }) {
  const { slug } = params;
  // ...
}
app/product/[...slug]/page.js
export function generateStaticParams() {
  return [{ slug: ["a", "1"] }, { slug: ["b", "2"] }, { slug: ["c", "3"] }];
}
 
// 使用 `generateStaticParams` 返回的 `params`
// 静态生成此页面的三个版本
// - /product/a/1
// - /product/b/2
// - /product/c/3
export default function Page({ params }) {
  const { slug } = params;
  // ...
}

示例

静态渲染

在构建时生成所有路径

要在构建时静态渲染所有路径,请向 generateStaticParams 提供完整的路径列表:

app/blog/[slug]/page.tsx
export async function generateStaticParams() {
  const posts = await fetch("https://.../posts").then((res) => res.json());
 
  return posts.map((post) => ({
    slug: post.slug,
  }));
}

在构建时生成部分路径

要在构建时静态渲染部分路径,并在运行时首次访问时渲染其余部分,请返回部分路径列表:

app/blog/[slug]/page.tsx
export async function generateStaticParams() {
  const posts = await fetch("https://.../posts").then((res) => res.json());
 
  // 在构建时渲染前 10 篇文章
  return posts.slice(0, 10).map((post) => ({
    slug: post.slug,
  }));
}

然后,通过使用 dynamicParams 段配置选项,你可以控制访问未由 generateStaticParams 生成的动态段时会发生什么。

app/blog/[slug]/page.js
// 除了前 10 篇文章外,其他所有文章都将返回 404
export const dynamicParams = false;
 
export async function generateStaticParams() {
  const posts = await fetch("https://.../posts").then((res) => res.json());
  const topPosts = posts.slice(0, 10);
 
  return topPosts.map((post) => ({
    slug: post.slug,
  }));
}

在运行时生成所有路径

要在首次访问时静态渲染所有路径,请返回一个空数组 (构建时不会渲染任何路径) 或使用 export const dynamic = 'force-static'

app/blog/[slug]/page.js
export async function generateStaticParams() {
  return [];
}

值得注意的是: 你必须始终从 generateStaticParams 返回一个数组,即使它是空的。否则,路由将被动态渲染。

app/changelog/[slug]/page.js
export const dynamic = "force-static";

禁用未指定路径的渲染

要防止未指定的路径在运行时被静态渲染,请在路由段中添加 export const dynamicParams = false 选项。使用此配置选项时,只有 generateStaticParams 提供的路径才会被服务,未指定的路由将返回 404 或匹配 ( 在 catch-all 路由 的情况下)。

路由中的多个动态段

你可以为当前布局或页面上方的动态段生成参数,但不能为下方的动态段生成参数。例如,对于 app/products/[category]/[product] 路由:

  • app/products/[category]/[product]/page.js 可以为 [category][product] 同时生成参数。
  • app/products/[category]/layout.js 只能[category] 生成参数。

有两种方法可以为具有多个动态段的路由生成参数:

自下而上生成参数

从子路由段生成多个动态段。

app/products/[category]/[product]/page.tsx
// 为 [category] 和 [product] 生成段
export async function generateStaticParams() {
  const products = await fetch("https://.../products").then((res) =>
    res.json()
  );
 
  return products.map((product) => ({
    category: product.category.slug,
    product: product.id,
  }));
}
 
export default function Page({
  params,
}: {
  params: { category: string; product: string };
}) {
  // ...
}
app/products/[category]/[product]/page.js
// 为 [category] 和 [product] 生成段
export async function generateStaticParams() {
  const products = await fetch("https://.../products").then((res) =>
    res.json()
  );
 
  return products.map((product) => ({
    category: product.category.slug,
    product: product.id,
  }));
}
 
export default function Page({ params }) {
  // ...
}

自上而下生成参数

首先生成父段,然后使用结果生成子段。

app/products/[category]/layout.tsx
// 为 [category] 生成段
export async function generateStaticParams() {
  const products = await fetch("https://.../products").then((res) =>
    res.json()
  );
 
  return products.map((product) => ({
    category: product.category.slug,
  }));
}
 
export default function Layout({ params }: { params: { category: string } }) {
  // ...
}
app/products/[category]/layout.js
// 为 [category] 生成段
export async function generateStaticParams() {
  const products = await fetch("https://.../products").then((res) =>
    res.json()
  );
 
  return products.map((product) => ({
    category: product.category.slug,
  }));
}
 
export default function Layout({ params }) {
  // ...
}

子路由段的 generateStaticParams 函数会为父 generateStaticParams 生成的每个段执行一次。

generateStaticParams 函数可以使用从父 generateStaticParams 函数返回的 params 来动态生成自己的段。

app/products/[category]/[product]/page.tsx
// 使用从父段的 `generateStaticParams` 函数传递的 `params` 
// 为 [product] 生成段
export async function generateStaticParams({
  params: { category },
}: {
  params: { category: string };
}) {
  const products = await fetch(
    `https://.../products?category=${category}`
  ).then((res) => res.json());
 
  return products.map((product) => ({
    product: product.id,
  }));
}
 
export default function Page({
  params,
}: {
  params: { category: string; product: string };
}) {
  // ...
}
app/products/[category]/[product]/page.js
// 使用从父段的 `generateStaticParams` 函数传递的 `params` 
// 为 [product] 生成段
export async function generateStaticParams({ params: { category } }) {
  const products = await fetch(
    `https://.../products?category=${category}`
  ).then((res) => res.json());
 
  return products.map((product) => ({
    product: product.id,
  }));
}
 
export default function Page({ params }) {
  // ...
}

值得注意的是:对于所有以 generate 为前缀的函数、布局、页面和服务器组件,fetch 请求会自动为相同的数据进行 记忆化。如果无法使用 fetch,可以使用 React 的 cache 函数

Version History

版本变更
v13.0.0引入 generateStaticParams