跳至內容

部分預渲染

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

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

注意:部分預渲染 (Partial Prerendering, PPR) 是一項實驗性功能,可能會有所變動。它尚未準備好投入生產環境使用。

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 請求。這確保了每個動態元件不需要多次網路往返。

使用部分預渲染

漸進式採用 (版本 15) 路由設定選項,在佈局頁面中漸進式地採用部分預渲染。

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`。

啟用 PPR(版本 14)

對於版本 14,您可以透過將 `ppr` 選項添加到您的 `next.config.js` 檔案來啟用它。這將應用於您的應用程式中的所有路由。

next.config.ts
import type { NextConfig } from 'next'
 
const nextConfig: NextConfig = {
  experimental: {
    ppr: true,
  },
}
 
export default nextConfig

動態元件

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

例如,使用 `cookies` 或 `headers` 等函式

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: { 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 '...'
}