跳至内容

部分预渲染

部分预渲染 (PPR) 使您能够在同一路由中组合静态和动态组件。

在构建过程中,Next.js 会尽可能多地预渲染路由。如果检测到动态代码(例如读取传入请求),您可以使用React Suspense边界将相关组件包装起来。然后,Suspense 边界的回退将包含在预渲染的 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 请求中。这确保了每个动态组件不需要多次网络往返。

使用部分预渲染

增量采用(版本 15)

在 Next.js 15 中,您可以通过将布局页面中的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

启用 PPR(版本 14)

对于版本 14,您可以通过在 next.config.js 文件中添加 ppr 选项来启用它。这将应用于应用程序中的所有路由。

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 将包含在预渲染中。

例如,使用像 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: { 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 '...'
}