資料獲取、快取與重新驗證

資料獲取是任何應用程式的核心部分。本頁將介紹如何在 React 和 Next.js 中獲取、快取及重新驗證資料。

有四種方式可以獲取資料:

  1. 在伺服器端使用 fetch
  2. 在伺服器端使用第三方函式庫
  3. 在客戶端透過路由處理器
  4. 在客戶端使用第三方函式庫

在伺服器端使用 fetch 獲取資料

Next.js 擴展了原生的 fetch Web API,讓您可以為伺服器端的每個 fetch 請求配置快取重新驗證行為。React 則擴展了 fetch,使其在渲染 React 元件樹時自動記憶化 fetch 請求。

您可以在伺服器元件、路由處理器伺服器動作中使用 async/await 搭配 fetch

例如:

async function getData() {
  const res = await fetch('https://api.example.com/...')
  // 返回值*不會*被序列化
  // 您可以返回 Date、Map、Set 等

  if (!res.ok) {
    // 這將觸發最近的 `error.js` 錯誤邊界
    throw new Error('Failed to fetch data')
  }

  return res.json()
}

export default async function Page() {
  const data = await getData()

  return <main></main>
}

須知事項

  • Next.js 提供了在伺服器元件中獲取資料時可能需要的實用函式,例如 cookiesheaders。這些會導致路由動態渲染,因為它們依賴於請求時的資訊。
  • 在路由處理器中,fetch 請求不會被記憶化,因為路由處理器不屬於 React 元件樹。
  • 伺服器動作中,fetch 請求不會被快取(預設為 cache: no-store)。
  • 若要在 TypeScript 的伺服器元件中使用 async/await,您需要使用 TypeScript 5.1.3 或更高版本,以及 @types/react 18.2.8 或更高版本。

快取資料

快取會儲存資料,因此不需要在每次請求時都從資料來源重新獲取。

預設情況下,Next.js 會自動將 fetch 的返回值快取在伺服器的資料快取中。這意味著資料可以在建置時或請求時獲取、快取,並在每次資料請求時重複使用。

// 'force-cache' 是預設值,可以省略
fetch('https://...', { cache: 'force-cache' })

但也有例外情況,fetch 請求在以下情況下不會被快取:

什麼是資料快取?

資料快取是一個持久的 HTTP 快取。根據您的平台,快取可以自動擴展並跨多個區域共享

了解更多關於資料快取的資訊。

重新驗證資料

重新驗證是清除資料快取並重新獲取最新資料的過程。這在資料變更且您希望確保顯示最新資訊時非常有用。

快取的資料可以通過兩種方式重新驗證:

  • 基於時間的重新驗證:在經過一定時間後自動重新驗證資料。這適用於變更不頻繁且即時性不那麼關鍵的資料。
  • 按需重新驗證:根據事件(例如表單提交)手動重新驗證資料。按需重新驗證可以使用基於標籤或路徑的方法來一次性重新驗證一組資料。這適用於您希望盡快顯示最新資料的情況(例如當您的無頭 CMS 內容更新時)。

基於時間的重新驗證

要按時間間隔重新驗證資料,您可以使用 fetchnext.revalidate 選項來設定資源的快取生命週期(以秒為單位)。

fetch('https://...', { next: { revalidate: 3600 } })

或者,要重新驗證路由段中的所有 fetch 請求,您可以使用路由段配置選項

layout.js | page.js
export const revalidate = 3600 // 最多每小時重新驗證一次

如果在靜態渲染的路由中有多個 fetch 請求,且每個請求有不同的重新驗證頻率,則所有請求將使用最短的時間。對於動態渲染的路由,每個 fetch 請求將獨立重新驗證。

了解更多關於基於時間的重新驗證

按需重新驗證

可以在伺服器動作路由處理器中通過路徑 (revalidatePath) 或快取標籤 (revalidateTag) 按需重新驗證資料。

Next.js 有一個快取標籤系統,用於跨路由使 fetch 請求失效。

  1. 使用 fetch 時,您可以選擇用一個或多個標籤標記快取條目。
  2. 然後,您可以呼叫 revalidateTag 來重新驗證與該標籤關聯的所有條目。

例如,以下 fetch 請求添加了快取標籤 collection

export default async function Page() {
  const res = await fetch('https://...', { next: { tags: ['collection'] } })
  const data = await res.json()
  // ...
}

然後,您可以通過在伺服器動作中呼叫 revalidateTag 來重新驗證標記為 collectionfetch 呼叫:

'use server'

import { revalidateTag } from 'next/cache'

export default async function action() {
  revalidateTag('collection')
}

了解更多關於按需重新驗證

錯誤處理與重新驗證

如果在嘗試重新驗證資料時拋出錯誤,則將繼續從快取中提供最後一次成功生成的資料。在後續請求中,Next.js 將重試重新驗證資料。

選擇退出資料快取

fetch 請求在以下情況下不會被快取:

  • fetch 請求中添加了 cache: 'no-store'
  • 在個別 fetch 請求中添加了 revalidate: 0 選項。
  • fetch 請求位於使用 POST 方法的路由處理器中。
  • fetch 請求在使用 headerscookies 之後。
  • 使用了 const dynamic = 'force-dynamic' 路由段選項。
  • fetchCache 路由段選項配置為預設跳過快取。
  • fetch 請求使用了 AuthorizationCookie 標頭,並且元件樹中有未快取的請求在其之上。

個別 fetch 請求

要為個別 fetch 請求選擇退出快取,您可以將 fetch 中的 cache 選項設為 'no-store'。這將在每次請求時動態獲取資料。

layout.js | page.js
fetch('https://...', { cache: 'no-store' })

查看所有可用的 cache 選項,請參閱 fetch API 參考

多個 fetch 請求

如果在路由段(例如佈局或頁面)中有多個 fetch 請求,您可以使用路由段配置選項來配置該段中所有資料請求的快取行為。

不過,我們建議為每個 fetch 請求單獨配置快取行為。這樣可以更精細地控制快取行為。

在伺服器端使用第三方函式庫獲取資料

在使用不支援或未公開 fetch 的第三方函式庫(例如資料庫、CMS 或 ORM 客戶端)時,您可以使用路由段配置選項和 React 的 cache 函式來配置這些請求的快取和重新驗證行為。

資料是否被快取取決於路由段是靜態或動態渲染。如果路由段是靜態的(預設值),則請求的輸出將作為路由段的一部分被快取和重新驗證。如果路由段是動態的,則請求的輸出將不會被快取,並會在每次渲染該段時重新獲取。

您還可以使用實驗性的 unstable_cache API

範例

在以下範例中:

  • React 的 cache 函式用於記憶化資料請求。
  • 在佈局和頁面段中將 revalidate 選項設為 3600,意味著資料將被快取並最多每小時重新驗證一次。
import { cache } from 'react'

export const getItem = cache(async (id: string) => {
  const item = await db.item.findUnique({ id })
  return item
})

儘管 getItem 函式被呼叫了兩次,但只會向資料庫發送一個查詢。

import { getItem } from '@/utils/get-item'

export const revalidate = 3600 // 最多每小時重新驗證一次資料

export default async function Layout({
  params: { id },
}: {
  params: { id: string }
}) {
  const item = await getItem(id)
  // ...
}

在客戶端使用路由處理器獲取資料

如果需要在客戶端元件中獲取資料,您可以從客戶端呼叫路由處理器。路由處理器在伺服器上執行並將資料返回給客戶端。這在您不想向客戶端暴露敏感資訊(例如 API 令牌)時非常有用。

請參閱路由處理器文件以獲取範例。

伺服器元件與路由處理器

由於伺服器元件在伺服器上渲染,因此您不需要從伺服器元件呼叫路由處理器來獲取資料。相反,您可以直接在伺服器元件中獲取資料。

在客戶端使用第三方函式庫獲取資料

您也可以使用第三方函式庫(例如 SWRTanStack Query)在客戶端獲取資料。這些函式庫提供了自己的 API 來記憶化請求、快取、重新驗證和變更資料。

未來的 API

use 是一個 React 函式,接受並處理由函式返回的 promise。目前在客戶端元件中不建議fetch 包裝在 use 中,這可能會觸發多次重新渲染。了解更多關於 use 的資訊,請參閱 React 文件