靜態網站生成 (SSG)

範例

如果頁面使用 靜態生成,則頁面 HTML 會在 構建時 生成。這意味著在生產環境中,當你執行 next build 時會生成頁面 HTML。然後這個 HTML 會在每次請求時被重複使用,並且可以被 CDN 快取。

在 Next.js 中,你可以 有或無資料 地靜態生成頁面。讓我們來看看每種情況。

無資料的靜態生成

預設情況下,Next.js 會在不獲取資料的情況下使用靜態生成來預先渲染頁面。以下是一個範例:

function About() {
  return <div>About</div>
}

export default About

請注意,此頁面不需要獲取任何外部資料即可預先渲染。在這種情況下,Next.js 會在構建時為每個頁面生成一個單一的 HTML 檔案。

有資料的靜態生成

有些頁面需要獲取外部資料來進行預先渲染。有兩種情況,可能適用其中一種或兩種。在每種情況下,你可以使用 Next.js 提供的這些函數:

  1. 你的頁面 內容 依賴於外部資料:使用 getStaticProps
  2. 你的頁面 路徑 依賴於外部資料:使用 getStaticPaths(通常與 getStaticProps 一起使用)。

情況 1:頁面內容依賴於外部資料

範例:你的部落格頁面可能需要從 CMS(內容管理系統)獲取部落格文章列表。

// TODO: 需要先獲取 `posts`(通過調用某些 API 端點)
//       然後才能預先渲染此頁面。
export default function Blog({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li>{post.title}</li>
      ))}
    </ul>
  )
}

為了在預先渲染時獲取這些資料,Next.js 允許你從同一個檔案中 export 一個名為 getStaticPropsasync 函數。此函數會在構建時被調用,讓你可以將獲取的資料傳遞給頁面的 props 以進行預先渲染。

export default function Blog({ posts }) {
  // 渲染文章...
}

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

  // 通過返回 { props: { posts } },Blog 組件
  // 將在構建時接收到 `posts` 作為 prop
  return {
    props: {
      posts,
    },
  }
}

要了解更多關於 getStaticProps 的工作原理,請查閱 資料獲取文件

情況 2:頁面路徑依賴於外部資料

Next.js 允許你創建具有 動態路由 的頁面。例如,你可以創建一個名為 pages/posts/[id].js 的檔案來顯示基於 id 的單篇部落格文章。這將允許你在訪問 posts/1 時顯示 id: 1 的部落格文章。

要了解更多關於動態路由的資訊,請查閱 動態路由文件

然而,你可能希望在構建時預先渲染哪些 id 取決於外部資料。

範例:假設你只向數據庫添加了一篇部落格文章(id: 1)。在這種情況下,你只希望在構建時預先渲染 posts/1

之後,你可能會添加第二篇 id: 2 的文章。那麼你也會希望預先渲染 posts/2

因此,你預先渲染的頁面 路徑 依賴於外部資料。為了解決這個問題,Next.js 允許你從動態頁面(在此例中為 pages/posts/[id].js)中 export 一個名為 getStaticPathsasync 函數。此函數會在構建時被調用,讓你可以指定要預先渲染的路徑。

// 此函數會在構建時被調用
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 }
}

同樣在 pages/posts/[id].js 中,你需要導出 getStaticProps,以便可以獲取此 id 的文章資料並用它來預先渲染頁面:

export default function Post({ post }) {
  // 渲染文章...
}

export async function getStaticPaths() {
  // ...
}

// 此函數也會在構建時被調用
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 } }
}

要了解更多關於 getStaticPaths 的工作原理,請查閱 資料獲取文件

何時應該使用靜態生成?

我們建議盡可能使用 靜態生成(有或無資料),因為你的頁面可以構建一次並由 CDN 提供服務,這比讓伺服器在每次請求時渲染頁面要快得多。

你可以對許多類型的頁面使用靜態生成,包括:

  • 行銷頁面
  • 部落格文章和作品集
  • 電子商務產品列表
  • 幫助和文檔

你應該問自己:「我可以在用戶請求 之前 預先渲染此頁面嗎?」如果答案是肯定的,那麼你應該選擇靜態生成。

另一方面,如果你無法在用戶請求之前預先渲染頁面,則靜態生成 不是 一個好主意。也許你的頁面顯示頻繁更新的資料,並且頁面內容在每次請求時都會變化。

在這種情況下,你可以執行以下操作之一:

  • 使用 客戶端資料獲取 的靜態生成:你可以跳過預先渲染頁面的某些部分,然後使用客戶端 JavaScript 來填充它們。要了解更多關於這種方法,請查閱 資料獲取文件
  • 使用 伺服器端渲染:Next.js 會在每次請求時預先渲染頁面。由於頁面無法被 CDN 快取,速度會較慢,但預先渲染的頁面將始終是最新的。我們將在下面討論這種方法。

On this page