RSC(React Server Component)
ISR(Incremental Static Regeneration)
React Server Component
异步组件 是服务器组件的一个新特性, 允许在渲染中 await.
RSC 是一种新型的组件, 它在打包之前在独立于客户端应用程序或 SSR 服务器的环境中提前渲染, 在 next.js 中, 渲染工作进一步按路由段划分, 以实现流式和部分渲染
- 数据获取, 将数据获取移动到更靠近数据源的服务器上, 可以减少获取渲染所需数据的时间以及客户端需要发出的请求数量来提高性能
- 安全性, 在服务器上保留敏感数据和逻辑, 例如 token 和 API keys, 而不会将它们暴露给客户端
- 缓存, 通过在服务器上渲染, 结果可以被缓存并在后续请求和跨用户中重用, 减少每个请求的渲染和数据获取量
- 性能, 减少所需的客户端 javascript 的数量, 对于弱网环境或设备较弱的用户来说需要下载、解析和执行的客户端 javascript 较少
- 初始化页面加载和首次内容绘制(FCP), 在服务器上生成 HTML, 允许用户立即查看页面而无需等客户端下载、解析和执行渲染页面所需要的 javascript
- SEO 和 社交网络共享(SNS), 渲染的 HTML 可供搜索引擎机器人用来检索页面, 社交网络机器人可用于为页面生成社交卡预览
- 流式传输, 服务器组件允许将渲染工作分成块, 并在准备就绪时将其流式传输到客户端. 这允许用户提前查看页面的部分内容, 而无需等待整个页面在服务器上渲染
渲染策略
- Static Rendering, 路由在构建时渲染, 或在数据重新验证后在后台渲染, 结果被缓存. 以优化用户和服务器请求之间共享渲染的结果
- Dynamic Redering, 在请求时渲染路由, 针对个性化的数据或只有在请求时才知道的信息
- Streaming, 从服务器逐步渲染 UI, 并在准备就绪时流式传输给客户端, 允许用户在整个内容完整渲染之前立即看到页面的部分内容.
有助于提高初始化页面加载性能, 以及依赖于较慢数据获取的 UI. 可以使用 Suspense 组件和 loading.tsx 开启
渲染流程:
- 按单个路由段和 Suspense Boundaries 拆分块
- 每个块使用 RSC Payload 和 客户端组件 JavaScript 指令渲染 HTML, 然后返回给客户端
- 客户端立即显示路由的快速非交互式页面预览, 这仅适用于初始化页面加载
- RSC Payload 用于协调客户端和 RSC 树并更新 DOM, JavaScript 指令用于 hydrate 客户端组件并使应用程序具有交互性
RSC Payload
渲染 RSC 树的紧凑二进制表示形式, 包含
- RSC 的渲染结果
- 客户端组件应渲染的占位符及其 JavaScript 文件的引用
- 从 RSC 传递给客户端组件的任何信息
Server Function
是一个在服务器上执行的异步函数, 它们可以在服务器和客户端组件之间调用, 以处理 Next.js 应用程序中的表单和提交和数据突变
当使用 ‘use server’ 指令定义服务器函数时, 将自动创建一个指向服务器函数的引用, 并将该引用传递给客户端组件. 当在客户端组件调用该函数时, React 向服务器发送一个请求来执行该函数, 并返回结果
- 不限于 <form>, 可以从 Event Handlers、useEffect、第三方库和其他表单元素 <button> 调用
- 与 Next.js cache 和 revalidate 集成, 当调用一个 action 时, Next.js 可以在单个服务器往返中返回更新的 UI 和新数据
- 使用 HTTP 的 POST 方法调用
- 接收的参数 和 返回值 必须可由 React 序列化
- 可以在应用程序的任何地方重复使用
- 从其使用的 layout 和 page 继承 运行时
- 从其使用的 layout 和 page 继承路由段设置, 包含 maxDuration 等字段
为什么使用 POST
- 幂等性:POST 用于创建/修改数据,支持非幂等操作
- 请求体:POST 可以携带复杂的序列化数据
- 语义正确:Server Functions 通常执行有副作用的操作
1 | // app/invoices/page.tsx |
在 useEffect 中使用
1 | // app/view-count.tsx |
Components
Font 设置字体
Form 扩展了 HTML form 元素并提供了 UI 加载的预获取, 客户端提交的导航, 渐进式增强
- action string, Form 的行为和普通的 HTML form 一致将使用 get 请求 form 数据被编码为 url 的查询参数
- action function, Form 提交时将会执行 Server Action
Image 设置图片
Link 扩展了 HTML a 元素提供预获取和路由导航
Script 设置 js 资源
路由
pages router 和 app router 模式下, 组成路由段的 文件 必须包含一个默认导出的组件
pages router
pages router 模式下, pages 目录下所有的包含默认导出 React Component 的文件都将作为路由段可用.
- pages 以当前目录下的 文件名 创建路由段, 目录下的 index.tsx 创建页面
- [fileName] 动态路由, 动态路由参数使用 useRouter 访问.
- […fileName] 截获所有动态路由参数
- [[…fileName]] 可选的截获所有动态路由参数, 同时会截获不带任何动态参数的路由
- [fileName] 动态路由, 动态路由参数使用 useRouter 访问.
API route
任意在 pages/api 目录下的文件使用 文件名 映射为 /api/* 并作为 api 入口而不是页面,不会增加客户端打包的代码大小.
以 index 命名的文件名不会作为 api 路由段的一部分
在文件中导出 config 配置对象修改默认配置.
API route 不能使用特殊的 CORS,只能使用 same-origin. 可以自定义包装一个 request handler 使用 CORS.
API route 不能和静态导出一起使用, app router 中的 Route handler 可以.
request.method 处理不同的请求方式
1 | // pages/api/hello.ts |
1 | // pages/api/hello.ts |
- Dynamic API Routes
API route 支持动态路由,命名规则同 pages router 动态路由. 动态路由参数在 req.query 中获取.
1 | // pages/api/post/[...slug].ts |
- 匹配规则
pages/api/post/create.js匹配/api/post/createpages/api/post/[pid].js匹配/api/post/1,/api/post/abc, 不能匹配/api/post/createpages/api/post/[...slug].js匹配/api/post/1/2,/api/post/a/b/c, 不能匹配/api/post/create,/api/post/abc
NextApiRequest/NextApiResponse
用于传统的 Next.js API 路由(pages/api 目录下的路由)
- 基于 Node.js 的 http 模块
- 包含 Express 风格的请求处理方法
- 适用于 Pages Router 的 API 路由
- 基于 URL 解析
- 需要使用 bodyParser 中间件
app router
基于 React Server Component 的路由模式, 支持共享布局、嵌套路由、加载状态、错误处理等.
app router 模式下, app 目录下只有 page 文件包含一个默认导出 React Component 和 route 命名的文件才会被解析为路由段的 UI, 否则路由是 404, 其他命名文件不会被外部访问, 相对是安全的.
app 以当前目录下的 目录名 创建路由段, 目录下的 page.tsx 或 route.ts 创建页面
_folderName 私有目录, 当前目录及子目录被
路由解析 忽略, 将 _ 转义为%5F后命名目录路由段可正常访问(folderName) 路由分组, 目录名被
路由解析 忽略, 使用相同的布局@folderName 并行路由, 被
路由解析 忽略. 同时或有条件地 在同一 layout.tsx 中渲染一个或多个页面.不能够影响 url, 插槽和正常页面合并之后形成与路由相关的最终页面.
使用
插槽渲染页面, 硬导航时无法恢复未匹配路由的插槽的活动状态时使用插槽的 default.tsx 渲染.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// 访问 / 同时渲染 app/pages.tsx, @team/page.tsx, @analytics/page.tsx
export default function Layout({ children, team, analytics }: readonly<{
children: React.ReactNode;
team: React.ReactNode;
analytics: React.ReactNode;
}>) {
return (
<>
{children}
<div className="flex justify-center items-center">
{team}
{analytics}
</div>
</>
)
}[folderName] 动态路由, 动态路由参数可以在
layout.tsx,page.tsx,route.ts和generateMetadata中获取- […folderName] 截获所有动态路由参数
- [[…folerName]] 可选的截获所有动态路由参数, 同时会截获不带任何动态参数的路由
1
2
3
4
5
6
7
8
9
10/*
app
photo
[id]
page.tsx
doc
[[...slug]]
page.tsx
page.tsx
*/(..)folderName 拦截路由, 在另一个页面中使用布局渲染拦截当前路由
- (.)folderName 匹配同一级的路由
- (..)folderName 匹配上一级的路由
- (..)(..)folderName 匹配上上一级的路由
- (…)folderName 匹配根路由
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15// 在 app/page.tsx 软导航 /photo/110 将渲染拦截路由 @modal/(.)photo/[id]/page.tsx 下的内容
// 硬导航 /photo/110 时渲染 app/photo/[id]/page.tsx
/*
app
@modal
default.tsx // 返回 null 在未匹配到 插槽 时不渲染内容
(.)photo
[id]
page.tsx
photo
[id]
page.tsx
layout.tsx
page.tsx
*/
Route handler
功能类似于 API route, 但是只能用在 app router 模式下,并且在同一层级下不能和 page.tsx 同时存在.
- 动态路由参数在第二个参数 context 中获取.
1 | // app/blog/[id].ts |
NextRequest/NextResponse
用于 App Router 的中间件和路由处理器(Route Handler)
- 基于 Web API 的 Request/Response 对象
- 提供更现代的 Web 标准 API
- 适用于 App Router 和中间件
- 提供增强的 NextUrl 对象
- 自动解析请求(不需要 bodyParser)
路由段
直接在 layout, page, Route Handler 中导出以下配置修改行为.
1 | // 阻止页面预渲染, 如果使用 cookies, headers, searchParams prop, connection, draftMode, unstable_noStore 等函数页面自动被视为动态渲染 |
文件规范
Component hierarchy
1 | <Layout> |
应用根目录或 src 目录下
- middleware.ts 在请求完成之前在服务器上运行代码, 根据传入的请求修改响应, 对于实现自定义服务器端逻辑非常有用, 配合
matcher使用过滤指定范围的请求- request
- proxy.ts Next.js 16 之后代替 middleware.ts, 功能保持不变, 以更好地反映其用途
1 | import { NextRequest, NextResponse } from 'next/server' |
- instrumentation.ts 使用代码将可观察工具集成到应用程序中, 能够跟踪性能和行为, 并在生产中调试问题
- register, 导出一个函数, 该函数将在启动一个新的 Next.js 服务实例时调用一次
- onRequestError, 导出一个函数, 当 Next.js 服务器捕获到错误时将触发该函数, 该函数内的任务必须是同步执行的
- error
- request
- context
1 | // instrumentation.ts |
pages router conventions
- _app, 自定义 App 页面, app router 模式下使用 layout 代替
- _document, 自定义 document 页面, app router 模式下使用 layout 代替
- _error, 自定义错误页面, app router 模式下使用 error.js 代替
- 404, 404 Error page, app router 模式下使用 not-found.js 代替
- 500, 500 Error page
app router conventions
layout.tsx 在多个页面之间共享布局的UI, 能够保持跨路由的状态、交互性, 不会重新渲染.
- Props
- children
- params 动态路由参数, 一个 Promise, Next.js 14 之前是同步的
- …slot 动态插槽
- Props
template.tsx 类似于 layout.tsx 能够包含布局和页面, 当路由发生改变时会重置状态
- Props
- children
- Props
route.ts 使用 Web Request 和 Response API 为给定的路由创建自定义请求处理程序, 和 page.tsx 不能同时存在
- request
- context
- params, 解析的当前路由的动态路由参数
page.tsx 定义路由独有的页面UI
- Props
- params 动态路由参数, 一个 Promise, Next.js 14 之前是同步的
- searchParams 当前 URL 的查询字符串参数, 一个 Promise, Next.js 14 之前是同步的
- Props
loading.tsx 创建基于加载时的状态, 配合
Suspense组件使用
1 | // loading.tsx |
not-found.tsx 路由未匹配到时渲染的 UI, 默认自动匹配 app 目录下的 not-found.tsx, 嵌套路由下手动调用 notFound 函数渲染局部 not-found.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15/*
app
products
comments
page.tsx // 调用 notFound 函数渲染当前路由段下的 not-found.tsx
not-found.tsx
layout.tsx
page.tsx
not-found.tsx
*/
export default function NotFound() {
return (
<div>Not Found</div>
)
}error.tsx 允许处理运行时的错误并显示回退 UI
- Props
- error
- reset
- Props
global-error.tsx 处理在根 layout 或 template 抛出的错误, 必须使用 html 和 body 标签, 这个文件将替换根 layout 或 template
1 | // app/blog/error.tsx |
- default.tsx 用于在 Nextjs 在加载完整页面后无法恢复
插槽的活动状态时使用. 刷新页面(硬导航)时, 为与当前 URL 未匹配的子页面渲染内容, 如果不存在则渲染 404- Props
- params 动态路由参数, 一个 Promise, Next.js 14 之前是同步的
- Props
1 | // 插槽不是路由段, 不能够影响 url, 插槽和正常页面合并之后形成与路由相关的最终页面, |
- mdx-components.tsx 在 app 目录同级定义一个解析 markdown 的组件, Next.js 15 开始支持, 只能用在 App Router 中
1 | // /app |
环境变量
环境变量自动加载到 route handler
- 使用 .env 加载环境变量
- 在 next.js 运行时外使用
@next/env包中的loadEnvConfig函数加载环境变量 - 只有以
NEXT_PUBLIC_开头的环境变量才会导出给客户端
加载顺序
- process.env
- .env.$(NODE_ENV).local
- .env.local (Not checked when NODE_ENV is test)
- .env.$(NODE_ENV)
- .env
dynamic APIs
动态 APIs 依赖于只能在请求时知道的信息(而不是在预渲染期间提前知道的信息), 使用这些 API 都表明了将在请求时选择整个路由进行动态渲染
- cookies
- headers
- connection 标记渲染内容等待用户的请求传入
- draftMode
- searchParams prop
- unstable_noStore 声明选择退出静态渲染, 并标识不应缓存特定组件, Next.js 15 使用 connection 代替
缓存
Request Memoization
是 React 的一个特性, next.js 扩展了 fetch API, 自动缓存相同的请求, 在 react 组件树中为相同的数据多次调用 fetch 函数将只执行一次
渲染路由时, 第一次调用特定请求时结果不在内存中而是缓存 MISS, 函数将被执行获取外部数据后存储到内存中,
在同一渲染过程中, 请求的后续函数调用将是缓存 HIT, 数据在不执行函数的情况下从内存中返回,
一旦路由被渲染并且渲染过程完成时, 内存将会被 重置, 所有请求记忆都会被清除
- 仅适用于 fetch 请求中的 GET 方法, 其他请求方法不会被记忆
- 仅适用于 React 组件树, 例如
generateMetadata、generateStaticParams、Layout、Page 和其他服务器组件中, route handler 不适用因为不属于 React 组件树
不推荐退出请求记忆
Data Cache 数据缓存
next.js 有一个内置的数据缓存, 可以在传入的服务器请求和部署中持久保持数据获取的结果
- 使用 fetch(‘’, {cache: ‘force-cache’}) 强制使用缓存
- 使用 fetch(‘’, {cache: ‘force-cache’, next: { revalidate: 3600 }}) 设置 next.js 验证数据的时间间隔(秒)
- cache mode, default | no-store | reload | no-cache | force-cache
- force-cache, 自己先在缓存中查找资源, 如果有不管是否过期直接返回
- default, 自己先在缓存中查找资源, 然后验证资源是否过期, 如果过期再询问服务器资源是否过期
- no-cache, 自己先在缓存中查找资源, 然后再询问服务器资源是否过期
- reload, 不查看缓存, 直接从服务器获取资源, 然后使用下载的资源更新缓存
- no-store, 不查看缓存, 直接从服务器获取资源, 并且不会更新缓存资源
退出数据缓存
- 使用 fetch 不指定 cache 参数或者指定 {cache: ‘no-store’}
Full Route Cache 完整路由缓存
next.js 在构建时自动渲染和缓存路由, 而不是在服务器上为每个请求渲染从而加快页面加载速度
- 使用流式
服务器组件载荷(RSC Payload) 和 Client Component 指令渲染 HTML, 返回响应而无需等待所有渲染完成 - 默认缓存路由的渲染结果
退出完整路由缓存
- 使用 dynamic APIs: cookies, headers, connection, draftMode, searchParams prop, unstable_noStore
- 在 layout、page、route Handler 中 export const dynamic = ‘force-dynamic’; 或者 export const revalidate = 0;
- 退出 Data Cache, 如果路由有一个未缓存的获取请求, 这将该路由退出完整路由缓存为每个请求获取特定数据, 其他未退出数据缓存的获取请求仍将缓存在数据缓存中
这允许缓存和未缓存数据的混合.
Router Cache 路由缓存
next.js 有一个客户端的路由缓存, 用于存储路由段的 RSC(React Server Component) 载荷, 按 layout、加载状态和 page 划分
当用户在路由之间导航时, next.js 会缓存访问过的路由段, 并预取用户可能导航到的路由, 导航之间不会重新加载整个页面, 并保留 React 状态和浏览器状态
- 布局被缓存并在导航时重用(部分渲染)
- 加载状态被缓存并在导航中重用, 以实现即时导航
- 默认页面不会被缓存, 但在浏览器向前和向后导航期间会被重用
函数
pages router Function
- getInitialProps(), 导出函数, 在服务端和客户端都运行,函数的返回结果将作为 React 组件的 props.
- ctx, 一个包含了 pathname, query, asPath, req, res, error 属性的上下文对象
1 | import { NextPageContext } from 'next' |
- getStaticProps(), 导出函数, Next.js 将在
构建时使用这个函数返回的结果预渲染这个页面组件, 可以直接在函数内获取数据- ctx, 一个包含了 params, preview, previewData, drafMode, locale, locales 等属性的对象
1 | import type { InferGetStaticPropsType, GetStaticProps } from 'next' |
- getStaticPaths(), 导出函数, 使用动态路由时, Next.js 将
静态预渲染这个函数返回的所有路径
1 | import type { |
- getServerSideProps(), 导出函数, 在每个路由请求时使用这个函数返回的结果
预渲染页面组件, 需要依赖请求的信息才能渲染页面时.- ctx, 一个包含了 params, req, res, query, preview, locale 等属性的上下文对象
1 | import type { InferGetServerSidePropsType, GetServerSideProps } from 'next' |
- withRouter(), 高阶组件, 向组件内注入 router, 通常使用 useRouter.
1 | import { withRouter } from 'next/router'; |
app router Function
- headers 一个 async 函数, 在服务器组件内读取请求头信息
- cookies 一个 async 函数, 在服务器组件内读取请求中的 cookies
1 | // page.tsx |
NextRequest 扩展了 Web Request API
NextResponse 扩展了 Web Response API
notFound 调用方法将抛出
NEXT_NOT_FOUND错误, 渲染 not-found.tsx 内容permanentRedirect 永久重定向, 返回 308(HTTP), 如果资源不存在可以使用 notFound 函数代替
redirect 重定向
- path
- type, replace(default) | push
revalidatePath 按需清理特定路径的缓存数据
- path
- type, page | layout
revalidateTag 按需清理特定缓存标记的缓存数据
- tag
after 注册在响应结束之后执行的任务, 通常记录日志和数据分析
1 | // layout.tsx |
- connection 标记渲染内容等待用户的请求传入
当不使用 dynamic APIs 时希望在运行时动态渲染而不是在构建时静态渲染, 通常用在访问有意更改渲染结果的外部信息时
1 | // page.tsx |
- drafMode 启用或禁用草稿模式(draftMode), async 函数
- isEnabled, 标识 draftMode 是否启用
- enable(), 启用 draftMode
- disable(), 禁用 draftMode
草稿模式允许在 next.js 应用程序中预览无头 CMS 中的草稿内容而无需重建整个网站, 对于在构建时静态渲染的内容允许切换到动态渲染并查看更改非常有用
1 | // page.tsx |
- fetch 扩展了 Web fetch API
- generateImageMetadata 生成一个或多个不同版本的图片元数据, 希望避免硬编码元数据时例如 Icon
- params, 一个 Promise, Next.js 14 之前是同步的
- 返回值
- id, string,required
- alt, string
- size, {width: number, height: number}
- contentType, string
1 | import {ImageResponse} from 'next/og'; |
generateMetadata 生成页面元数据
generateSitemaps 生成应用站点地图
generateStaticParams 合并动态路由段和静态路由, 在构建时生成路由而不是在请求时按需生成
- Props
- params 动态路由参数, 一个 Promise, Next.js 14 之前是同步的
- Props
1 | export async function generateStaticParams({ params }) { |
generateViewport 生成页面的视窗配置
ImageResponse 图片构造函数, 生成动态图片
import { ImageResponse } from 'next/og';unstable_cache 允许缓存昂贵操作的结果, 并在多个请求中重用它们, 使用
use cache代替- fetchData, 获取数据的异步函数
- keyPairs, 一个额外的密钥数组, 为缓存添加标识
- options 控制缓存的行为
- tags, 一组用于控制缓存失效的标签
- revalidate, 缓存应该被验证的时间间隔(秒)
1 | const data = unstable_cache(fetchData, keyParts, options)(); |
- unstable_noStore 声明选择退出静态渲染, 并标识不应缓存特定组件, Next.js 15 使用 connection 代替
1 | import { unstable_noStore as noStore } from 'next/cache'; |
hook
Client Component Hook
- useLinkStatus 跟踪 Link 组件的挂起状态, 当导航到新路由完成时展示内联的视觉反馈.
- useParams 获取动态路由参数
- usePathname 获取当前 url 的路径
- useReportWebVitals 获取网站性能指标
- useRouter 编程式改变路由
1 | 'use client'; |
useSearchParams 获取当前 url 查询参数
useSelectedLayoutSegment 获取当前 layout 下面一层的活动路由段, 通常用于在父布局中改变子段的状态
useSelectedLayoutSegments 获取当前 layout 下的活动路由段, 通常用于在父布局中改变子段的状态
1 | // app/blog/blog-nav-link.tsx |
- userAgent 获取 request 请求中的 user-agent
1 | import { NextRequest, NextResponse, userAgent } from "next/server"; |
元数据
元数据支持两种方式配置, 配置文件 和 动态生成
favicon
- 使用图片文件放在 app 目录下
- 使用代码生成图标文件
1 | // app/icon.tsx |
metadata
不能在相同的路由段中同时使用 静态配置 和 动态生成 两种方式, 从 layout.tsx 或 page.tsx 中导出
导出 metadata 时不能使用 'use client' 指令标识组件为客户端组件
- 静态配置
- 使用
generateMetadata函数动态生成
1 | // layout.tsx, page.tsx |
viewport 页面视窗
1 | // layout.tsx, page.tsx |
manifest
1 | // app/manifest.json |
robots
1 | // app/robots.txt |
sitemap
1 | // app/sitemap.xml |
configuration
- headers 允许给匹配的 path 添加头部信息
- rewrites 允许将传入的请求 url 重写为目标 url, 通常作为接口代理
- redirects 根据匹配的 url 重定向到目标 url
1 | // next.config.js |