跳至內容
建置您的應用程式渲染靜態網站生成 (SSG)

靜態網站生成 (SSG)

範例

如果頁面使用靜態生成 (Static Generation),頁面 HTML 會在建置時 (build time) 生成。這表示在生產環境中,當您執行 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: Need to fetch `posts` (by calling some API endpoint)
//       before this page can be pre-rendered.
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 }) {
  // Render posts...
}
 
// This function gets called at build time
export async function getStaticProps() {
  // Call an external API endpoint to get posts
  const res = await fetch('https://.../posts')
  const posts = await res.json()
 
  // By returning { props: { posts } }, the Blog component
  // will receive `posts` as a prop at build time
  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].jsexport 一個名為 getStaticPathsasync 函式。這個函式會在建置時被呼叫,並讓您可以指定要預渲染的路徑。

// This function gets called at build time
export async function getStaticPaths() {
  // Call an external API endpoint to get posts
  const res = await fetch('https://.../posts')
  const posts = await res.json()
 
  // Get the paths we want to pre-render based on posts
  const paths = posts.map((post) => ({
    params: { id: post.id },
  }))
 
  // We'll pre-render only these paths at build time.
  // { fallback: false } means other routes should 404.
  return { paths, fallback: false }
}

同樣在 pages/posts/[id].js 中,您需要匯出 getStaticProps,以便您可以擷取有關此 id 的文章資料,並使用它來預渲染頁面。

export default function Post({ post }) {
  // Render post...
}
 
export async function getStaticPaths() {
  // ...
}
 
// This also gets called at build time
export async function getStaticProps({ params }) {
  // params contains the post `id`.
  // If the route is like /posts/1, then params.id is 1
  const res = await fetch(`https://.../posts/${params.id}`)
  const post = await res.json()
 
  // Pass post data to the page via props
  return { props: { post } }
}

要瞭解更多關於 getStaticPaths 如何運作的資訊,請查閱資料擷取文件

我應該在什麼時候使用靜態生成?

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

您可以將靜態生成用於許多類型的頁面,包括:

  • 行銷頁面
  • 部落格文章和作品集
  • 電子商務產品列表
  • 說明和文件

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

另一方面,如果您無法在使用者請求之前預渲染頁面,那麼靜態生成就不是一個好主意。也許您的頁面顯示經常更新的資料,並且頁面內容在每次請求時都會更改。

在這種情況下,您可以執行下列其中一項操作:

  • 使用搭配客戶端資料擷取的靜態生成:您可以略過預渲染頁面的某些部分,然後使用客戶端 JavaScript 來填入它們。要瞭解更多關於這種方法的資訊,請查閱資料擷取文件
  • 使用伺服器端渲染:Next.js 會在每次請求時預渲染頁面。它會比較慢,因為頁面無法由 CDN 快取,但預渲染的頁面將始終保持最新狀態。我們將在下面討論這種方法。