跳到內容

部分預先渲染

注意: 部分預先渲染是一項實驗性功能,僅在 Canary 版本中提供,且可能會變更。尚未準備好用於生產環境。

部分預先渲染 (PPR) 可讓您在同一個路由中結合靜態和動態元件。

在建置期間,Next.js 會盡可能預先渲染路由。如果偵測到動態程式碼,例如從傳入的請求讀取,您可以將相關元件包裝在 React Suspense 邊界中。然後,Suspense 邊界的 fallback 將包含在預先渲染的 HTML 中。

Partially Prerendered Product Page showing static nav and product information, and dynamic cart and recommended products

🎥 觀看: 為什麼使用 PPR 以及其運作方式 → YouTube (10 分鐘)

背景

PPR 使您的 Next.js 伺服器能夠立即傳送預先渲染的內容。

為了防止用戶端到伺服器的瀑布效應,動態元件在伺服器提供初始預先渲染的同時,開始從伺服器平行串流。這確保了動態元件可以在用戶端 JavaScript 在瀏覽器中載入之前開始渲染。

為了防止為每個動態元件建立許多 HTTP 請求,PPR 能夠將靜態預先渲染和動態元件組合到單個 HTTP 請求中。這確保了每個動態元件不需要多個網路往返。

使用部分預先渲染

漸進式採用 (Version 15 Canary 版本)

在 Next.js 15 Canary 版本中,PPR 作為實驗性功能提供。它在穩定版本中尚不可用。若要安裝

npm install next@canary

您可以透過在 next.config.js 中將 ppr 選項設定為 incremental,並在檔案頂端匯出 experimental_ppr 路由設定選項,在版面配置頁面中逐步採用部分預先渲染

next.config.ts
import type { NextConfig } from 'next'
 
const nextConfig: NextConfig = {
  experimental: {
    ppr: 'incremental',
  },
}
 
export default nextConfig
app/page.tsx
import { Suspense } from 'react'
import { StaticComponent, DynamicComponent, Fallback } from '@/app/ui'
 
export const experimental_ppr = true
 
export default function Page() {
  return (
    <>
      <StaticComponent />
      <Suspense fallback={<Fallback />}>
        <DynamicComponent />
      </Suspense>
    </>
  )
}

要知道:

  • 沒有 experimental_ppr 的路由將預設為 false,並且不會使用 PPR 進行預先渲染。您需要為每個路由明確選擇加入 PPR。
  • experimental_ppr 將適用於路由區段的所有子項目,包括巢狀版面配置和頁面。您不必將其新增至每個檔案,只需新增至路由的頂層區段即可。
  • 若要停用子區段的 PPR,您可以在子區段中將 experimental_ppr 設定為 false

動態元件

next build 期間為您的路由建立預先渲染時,Next.js 要求動態 API 必須以 React Suspense 包裝。然後,fallback 會包含在預先渲染中。

例如,使用 cookiesheaders 等函式

app/user.tsx
import { cookies } from 'next/headers'
 
export async function User() {
  const session = (await cookies()).get('session')?.value
  return '...'
}

此元件需要查看傳入的請求以讀取 Cookie。若要將其與 PPR 一起使用,您應該使用 Suspense 包裝元件

app/page.tsx
import { Suspense } from 'react'
import { User, AvatarSkeleton } from './user'
 
export const experimental_ppr = true
 
export default function Page() {
  return (
    <section>
      <h1>This will be prerendered</h1>
      <Suspense fallback={<AvatarSkeleton />}>
        <User />
      </Suspense>
    </section>
  )
}

元件僅在存取值時才選擇加入動態渲染。

例如,如果您從 page 讀取 searchParams,您可以將此值作為 prop 轉發到另一個元件

app/page.tsx
import { Table } from './table'
 
export default function Page({
  searchParams,
}: {
  searchParams: Promise<{ sort: string }>
}) {
  return (
    <section>
      <h1>This will be prerendered</h1>
      <Table searchParams={searchParams} />
    </section>
  )
}

在表格元件內部,從 searchParams 存取值會使元件動態執行

app/table.tsx
export async function Table({
  searchParams,
}: {
  searchParams: Promise<{ sort: string }>
}) {
  const sort = (await searchParams).sort === 'true'
  return '...'
}