部分预渲染 (Partial Prerendering, PPR) 使你能够将静态和动态组件组合在同一路由中。
在构建过程中,Next.js 会尽可能多地预渲染路由内容。如果检测到动态代码,比如读取传入的请求,你可以用 React Suspense 边界包裹相关组件。然后,Suspense 边界的后备内容将包含在预渲染的 HTML 中。
注意: 部分预渲染是一个实验性功能,可能会发生变化。它尚未准备好用于生产环境。
** 观看:** 为什么使用 PPR 及其工作原理 → YouTube (10分钟)。
1. 背景
PPR 使你的 Next.js 服务器能够立即发送预渲染的内容。
为了防止客户端到服务器的延迟,动态组件会在服务器上并行流式传输,同时提供初始的预渲染内容。这确保动态组件可以在客户端 JavaScript 加载到浏览器之前开始渲染。
为了避免为每个动态组件创建多个 HTTP 请求,PPR 能够将静态预渲染和动态组件组合在一起,形成一个单独的 HTTP 请求。这确保每个动态组件不需要多个网络往返。
2. 使用部分预渲染
2.1 增量采用(版本 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
。
2.2 启用 PPR(版本 14)
对于版本 14,你可以通过将 ppr
选项添加到你的 next.config.js
文件中来启用它。这将适用于应用中的所有路由:
// next.config.ts import type { NextConfig } from 'next' const nextConfig: NextConfig = { experimental: { ppr: true, }, } export default nextConfig
3. 动态组件
在 next build
期间创建路由的预渲染器时,Next.js 需要将动态函数用 React Suspense 包裹起来。然后,fallback
将被包括在预渲染中。
例如,使用 cookies()
或 headers()
函数:
// app/user.tsx import { cookies } from 'next/headers' export function User() { const session = cookies().get('session')?.value return '...' }
这个组件需要查看传入的请求来读取 cookies。要使用 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 function Table({ searchParams }: { searchParams: { sort: string } }) { const sort = searchParams.sort === 'true' return '...' }
4. 处理动态渲染
要确保动态组件能够正确渲染,你需要将其与 React Suspense 结合使用,并在 PPR 环境下对其进行适当配置。这包括对函数、请求和组件的处理,以确保在动态数据加载时能够显示合适的后备内容。
4.1 使用 Suspense 包裹动态组件
在使用 PPR 时,确保将所有动态组件用 Suspense
包裹,以便在动态内容加载期间显示一个后备内容。以下是一个使用 Suspense 包裹动态组件的示例:
// app/page.tsx import { Suspense } from 'react' import { DynamicComponent, Fallback } from './dynamic-component' export const experimental_ppr = true export default function Page() { return ( <section> <h1>This will be prerendered</h1> <Suspense fallback={<Fallback />}> <DynamicComponent /> </Suspense> </section> ) }
4.2 使用动态数据
在动态渲染过程中,你可能需要处理来自请求的动态数据。例如,处理 cookies 或 headers 时,你应确保将其包装在 Suspense 组件中。
// app/dynamic-component.tsx import { cookies } from 'next/headers' export function DynamicComponent() { const session = cookies().get('session')?.value return <div>{session ? `Welcome back, ${session}` : 'Please log in'}</div> }
4.3 处理嵌套组件
如果你的动态数据需要传递到嵌套组件中,你可以将动态数据作为 prop 传递给子组件。在这种情况下,确保子组件也被适当地处理,并且能够正确渲染动态内容。
// app/page.tsx import { Suspense } from 'react' import { Table } from './table' import { getSearchParams } from './utils' export const experimental_ppr = true export default function Page() { const searchParams = getSearchParams() return ( <section> <h1>This will be prerendered</h1> <Suspense fallback={<div>Loading...</div>}> <Table searchParams={searchParams} /> </Suspense> </section> ) }
// app/table.tsx export function Table({ searchParams }: { searchParams: { sort: string } }) { const sort = searchParams.sort === 'true' return <div>Sorting by: {sort ? 'ascending' : 'descending'}</div> }
5. 测试和调试
在使用 PPR 时,确保进行充分的测试,以验证预渲染和动态渲染的效果是否符合预期。调试过程可能需要查看控制台日志、网络请求和组件状态,以确保所有动态内容都能正确加载和渲染。
总结
部分预渲染 (PPR) 是 Next.js 的一个实验性特性,允许将静态和动态内容结合在同一路由中。通过使用 React Suspense 和配置 experimental_ppr
选项,你可以优化页面加载时间和用户体验。请注意,该特性仍在开发中,可能会发生变化,因此在生产环境中使用时请确保测试充分。
如果你有任何问题或需要进一步的帮助,欢迎随时提问!