getStaticPaths

當從使用動態路由 (Dynamic Routes)的頁面導出名為 getStaticPaths 的函數時,Next.js 會靜態預渲染由 getStaticPaths 指定的所有路徑。

import type {
  InferGetStaticPropsType,
  GetStaticProps,
  GetStaticPaths,
} from 'next'

type Repo = {
  name: string
  stargazers_count: number
}

export const getStaticPaths = (async () => {
  return {
    paths: [
      {
        params: {
          name: 'next.js',
        },
      }, // 請參閱下方的 "paths" 部分
    ],
    fallback: true, // false 或 "blocking"
  }
}) satisfies GetStaticPaths

export const getStaticProps = (async (context) => {
  const res = await fetch('https://api.github.com/repos/vercel/next.js')
  const repo = await res.json()
  return { props: { repo } }
}) satisfies GetStaticProps<{
  repo: Repo
}>

export default function Page({
  repo,
}: InferGetStaticPropsType<typeof getStaticProps>) {
  return repo.stargazers_count
}

getStaticPaths 返回值

getStaticPaths 函數應返回一個包含以下必需屬性的物件:

paths

paths 鍵決定了哪些路徑會被預渲染。例如,假設您有一個使用動態路由 (Dynamic Routes)的頁面,名為 pages/posts/[id].js。如果您從此頁面導出 getStaticPaths 並為 paths 返回以下內容:

return {
  paths: [
    { params: { id: '1' }},
    {
      params: { id: '2' },
      // 如果配置了 i18n,也可以返回路徑的語言環境
      locale: "en",
    },
  ],
  fallback: ...
}

那麼,Next.js 將在 next build 期間使用 pages/posts/[id].js 中的頁面組件靜態生成 /posts/1/posts/2

每個 params 物件的值必須與頁面名稱中使用的參數匹配:

  • 如果頁面名稱是 pages/posts/[postId]/[commentId],則 params 應包含 postIdcommentId
  • 如果頁面名稱使用全捕捉路由 (catch-all routes),例如 pages/[...slug],則 params 應包含 slug(這是一個陣列)。如果此陣列是 ['hello', 'world'],則 Next.js 將靜態生成位於 /hello/world 的頁面。
  • 如果頁面使用可選全捕捉路由 (optional catch-all route),則使用 null[]undefinedfalse 來渲染根路由。例如,如果您為 pages/[[...slug]] 提供 slug: false,Next.js 將靜態生成頁面 /

params 的字串是區分大小寫的,理想情況下應進行標準化以確保路徑正確生成。例如,如果為參數返回 WoRLD,則僅當實際訪問的路徑是 WoRLD 時才會匹配,而不是 worldWorld

除了 params 物件外,當配置了 i18n 時,還可以返回一個 locale 字段,該字段配置了正在生成的路徑的語言環境。

fallback: false

如果 fallbackfalse,則任何未由 getStaticPaths 返回的路徑將導致404 頁面

當運行 next build 時,Next.js 將檢查 getStaticPaths 是否返回了 fallback: false,然後僅構建由 getStaticPaths 返回的路徑。如果您需要創建的路徑數量較少,或者不經常添加新的頁面資料,此選項非常有用。如果您發現需要添加更多路徑,並且您有 fallback: false,則需要再次運行 next build 以便生成新路徑。

以下示例預渲染了一個名為 pages/posts/[id].js 的每頁一篇博客文章。博客文章列表將從 CMS 獲取並由 getStaticPaths 返回。然後,對於每個頁面,它使用 getStaticProps 從 CMS 獲取文章資料。

pages/posts/[id].js
function Post({ post }) {
  // 渲染文章...
}

// 此函數在構建時被調用
export async function getStaticPaths() {
  // 調用外部 API 端點以獲取文章
  const res = await fetch('https://.../posts')
  const posts = await res.json()

  // 根據文章獲取我們想要預渲染的路徑
  const paths = posts.map((post) => ({
    params: { id: post.id },
  }))

  // 我們將在構建時僅預渲染這些路徑。
  // { fallback: false } 表示其他路由應返回 404。
  return { paths, fallback: false }
}

// 此函數也在構建時被調用
export async function getStaticProps({ params }) {
  // params 包含文章的 `id`。
  // 如果路由是 /posts/1,則 params.id 是 1
  const res = await fetch(`https://.../posts/${params.id}`)
  const post = await res.json()

  // 通過 props 將文章資料傳遞給頁面
  return { props: { post } }
}

export default Post

fallback: true

範例

如果 fallbacktrue,則 getStaticProps 的行為會發生以下變化:

  • getStaticPaths 返回的路徑將在構建時由 getStaticProps 渲染為 HTML
  • 在構建時未生成的路徑不會導致 404 頁面。相反,Next.js 將在首次請求此類路徑時提供一個「fallback」版本的頁面。像 Google 這樣的網路爬蟲不會收到 fallback,而是該路徑的行為將如同 fallback: 'blocking'
  • 當通過 next/linknext/router(客戶端)導航到具有 fallback: true 的頁面時,Next.js 將不會提供 fallback,而是該頁面的行為將如同 fallback: 'blocking'
  • 在後台,Next.js 將靜態生成請求的路徑 HTMLJSON。這包括運行 getStaticProps
  • 完成後,瀏覽器將收到生成路徑的 JSON。這將用於自動渲染具有所需 props 的頁面。從用戶的角度來看,頁面將從 fallback 頁面切換到完整頁面。
  • 同時,Next.js 將此路徑添加到預渲染頁面列表中。對同一路徑的後續請求將像其他在構建時預渲染的頁面一樣提供生成的頁面。

須知:當使用 output: 'export' 時,不支持 fallback: true

何時使用 fallback: true

如果您的應用程式有大量依賴於資料的靜態頁面(例如非常大的電子商務網站),fallback: true 非常有用。如果您想預渲染所有產品頁面,構建將花費很長時間。

相反,您可以靜態生成一小部分頁面,並對其餘頁面使用 fallback: true。當有人請求尚未生成的頁面時,用戶將看到帶有加載指示器或骨架組件的頁面。

不久之後,getStaticProps 完成,頁面將使用請求的資料進行渲染。從現在開始,任何請求同一頁面的人都將獲得靜態預渲染的頁面。

這確保了用戶始終擁有快速的體驗,同時保留了快速構建和靜態生成的好處。

fallback: true 不會更新已生成的頁面,如需更新,請參閱增量靜態再生 (Incremental Static Regeneration)

fallback: 'blocking'

如果 fallback'blocking',則未由 getStaticPaths 返回的新路徑將等待 HTML 生成,與 SSR 相同(因此稱為blocking),然後緩存以供將來請求使用,因此每個路徑僅發生一次。

getStaticProps 的行為如下:

  • getStaticPaths 返回的路徑將在構建時由 getStaticProps 渲染為 HTML
  • 在構建時未生成的路徑不會導致 404 頁面。相反,Next.js 將在首次請求時進行 SSR 並返回生成的 HTML
  • 完成後,瀏覽器將收到生成路徑的 HTML。從用戶的角度來看,它將從「瀏覽器正在請求頁面」過渡到「完整頁面已加載」。沒有加載/fallback 狀態的閃爍。
  • 同時,Next.js 將此路徑添加到預渲染頁面列表中。對同一路徑的後續請求將像其他在構建時預渲染的頁面一樣提供生成的頁面。

默認情況下,fallback: 'blocking' 不會更新已生成的頁面。要更新生成的頁面,請將 fallback: 'blocking'增量靜態再生 (Incremental Static Regeneration) 一起使用。

須知:當使用 output: 'export' 時,不支持 fallback: 'blocking'

Fallback 頁面

在頁面的「fallback」版本中:

  • 頁面的 props 將為空。
  • 使用 router,您可以檢測是否正在渲染 fallback,router.isFallback 將為 true

以下示例展示了如何使用 isFallback

pages/posts/[id].js
import { useRouter } from 'next/router'

function Post({ post }) {
  const router = useRouter()

  // 如果頁面尚未生成,這將在初始時顯示
  // 直到 getStaticProps() 運行完成
  if (router.isFallback) {
    return <div>載入中...</div>
  }

  // 渲染文章...
}

// 此函數在構建時被調用
export async function getStaticPaths() {
  return {
    // 僅 `/posts/1` 和 `/posts/2` 在構建時生成
    paths: [{ params: { id: '1' } }, { params: { id: '2' } }],
    // 啟用靜態生成其他頁面
    // 例如:`/posts/3`
    fallback: true,
  }
}

// 此函數也在構建時被調用
export async function getStaticProps({ params }) {
  // params 包含文章的 `id`。
  // 如果路由是 /posts/1,則 params.id 是 1
  const res = await fetch(`https://.../posts/${params.id}`)
  const post = await res.json()

  // 通過 props 將文章資料傳遞給頁面
  return {
    props: { post },
    // 如果收到請求,最多每秒重新生成文章一次
    revalidate: 1,
  }
}

export default Post

版本歷史

版本變更
v13.4.0App Router 現已穩定,簡化了資料獲取,包括 generateStaticParams()
v12.2.0按需增量靜態再生 (On-Demand Incremental Static Regeneration) 現已穩定。
v12.1.0新增按需增量靜態再生 (On-Demand Incremental Static Regeneration)(測試版)。
v9.5.0穩定的增量靜態再生 (Incremental Static Regeneration)
v9.3.0引入 getStaticPaths

On this page