优点:
- 直观的、 基于页面的路由系统(并支持动态路由)
- 预渲染。支持在页面级的静态生成 (SSG) 和服务器端渲染 (SSR)
- 自动代码拆分,提升页面加载速度
- 具有经过优化的预取功能的客户端路由
- 内置 CSS 和 Sass 的支持,并支持任何CSS-in-JS库
- 开发环境支持快速刷新
- 利用 Serverless Functions 及 API 路由构建 API 功能
- 完全可扩展
安装
- 前提:node 版本需要12.22.0 及更高
- 创建:npx create-next-app@latest –typescript
- 运行:npm run dev启动服务器,访问
http://localhost:3000
- 安装:npm install next react react-dom
- 配置:在package.json文件添加
scripts
配置1
2
3
4
5
6"scripts": {
"dev": "next dev", // 开发模式启动
"build": "next build", // 构建生产环境
"start": "next start", // 启动生产环境服务器
"lint": "next lint" // 设置内置ESLint配置
}
页面(Pages)
- 在next.js中,page就是React组件,文件存放在
pages
目录下,每个page都使用其文件名作为路由,通过/文件名访问- 支持有动态路由的pages,如文件名为
pages/posts/[id].js
,通过posts/1
访问
- 支持有动态路由的pages,如文件名为
预渲染
- 默认情况下将预渲染每个page,即预先为每个page生成HTML文件
- 水合:当浏览器加载一个page时,其Javascript代码将运行并使页面完全具有交互性的过程。
- 两种形式的预渲染:
- 静态生成(推荐):HTML在构建时生成,并在每次页面请求时重用
- 服务端渲染:在每次页面请求时重新生成HTML
- 允许每个页面选择渲染方式,CDN可以在没有额外配置的情况下缓存静态生成的页面提高性能
静态生成 — 生成带有或不带有数据的页面
- 生成不带有数据的静态页面:不涉及获取外部数据,只需构建时为页面生成HTML文件
- 生成带有数据的页面:
- 页面内容取决于外部数据: 使用getStaticProps
- 页面paths取决于外部数据:使用getStaticPaths + getStaticProps
- 尽可能使用静态生成如:营销页面,博客文章、简历、产品列表、帮助文档
- 静态生成不满足使用的解决方案:静态生成+客户端渲染,服务端渲染
服务端渲染 — 也被称为SSR或动态渲染
- 使用:getServerSideProps
数据获取
- getStaticProps(静态生成):仅在
构建时
获取数据。 - getStaticPaths(静态生成):根据数据指定预渲染页面的动态路由。
- getServerSideProps(服务器端渲染):获取每个请求的数据。
getStaticProps
- 返回一个对象
- context包含:
- params: 使用动态路由页面的路由参数
- preview:是否处于预览模式。true/undefined
- previewData:setPreviewData设置的预览数据集
- locale:活动区域设置(前提:启用国际路由)
- locales:所有支持的区域设置(前提:启用国际路由)
- defaultLocale:配置的默认区域设置(前提:启用国际路由)
- props:可选对象,包含页面组件将接受的props
- revalidate:可选的次数,单位秒,默认false,具体看增量静态生成
- notFound:可选的布尔值,允许页面返回404和页面
- redirect:可选的重定向值,允许重定向到内部和外部资源,构建时不允许重定向
1
2
3
4
5
6
7
8
9
10// js
export async function getStaticProps(context) {
return {
props: {}
}
}
// ts
import { GetStaticProps } from 'next'
export const getStaticProps: GetStaticProps = async (context) => {}
增量静态再生(ISR)
– 基于每个页面使用静态生成,而无需重新构建整个站点。- 使用revalidate:number启用,当对在生成时预呈现的页面发出请求时,它最初将显示缓存的页面。过程如下:
- 初始请求之后和10秒之前对页面的任何请求也会被缓存并即时缓存
- number秒窗口之后,下一个请求仍将显示缓存(过时)页面
- next会在后台触发页面重新生成
- 生成成功后,next使缓存失效并显示更新的产品页面;失败则页面保持不变
- 使用revalidate:number启用,当对在生成时预呈现的页面发出请求时,它最初将显示缓存的页面。过程如下:
读取文件:process.cwd()
- 文件可以直接从getStaticProps中的文件系统中读取。即必须获取文件的完整路径
- next将代码编译到一个单独的目录中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19import path from 'path'
export async function getStaticProps() {
const postsDirectory = path.join(process.cwd(),'posts')
// 使用 fs.readdir(postsDirectory)来读取这个目录
const filenames = await fs.readdir(postsDirectory)
const posts = filenames.map(async (filename) => {
const filePath = path.join(postsDirectory, filename)
const fileContents = await fs.readFile(filePath, 'utf8')
return {
filename,
content: fileContents,
}
})
return {
props: {
posts: await Promise.all(posts),
},
}
}
getStaticPaths
- 静态预呈现使用动态路由的页面时使用
- paths键(必选)
- 如果页面使用可选的捕获所有路由,使用null、[]、undefined或 false来呈现最根路径,如果在
page/[[...slug]]
,使用slug:false,则静态生成页面”/“
- 如果页面使用可选的捕获所有路由,使用null、[]、undefined或 false来呈现最根路径,如果在
- fallback键(必选)
- fallback:false:则任何未返回的路径都将导致404,不经常添加新页面可用
- fallback: true:返回的路径将在构建时由getStaticProps呈现为HTML,在生成时未生成的路径不会生成404,提供回退版本,从用户角度看,页面将从回退页面交换到整个页面。有大量依赖于数据的静态页面可用。
- fallback:blocking:阻塞原因,每条路径只发生一次。构建时尚未生成的路径不会导致404。相反Next将对第一个请求进行SSR,并返回生成的HTML。完成后,浏览器将接收生成路径的 HTML。Next将此路径添加到预呈现页面的列表中。
-
回退页面
- 页面的props将为空。使用router.isFallback可以检测是否正在渲染回退,如果只为true的话
1
2
3
4
5
6
7
8
9
10
11
12// js
export async function getStaticPaths() {
return {
paths: [
{ params: { ... }}
],
fallback: true,false,or 'blocking'
}
}
// ts
import { GetStaticPaths } from 'next'
export const getStaticPaths: GetStaticPaths = async () => {}
- 页面的props将为空。使用router.isFallback可以检测是否正在渲染回退,如果只为true的话
getServerSideProps
- 仅当需要预呈现其数据必须在请求时提取的页面时,才应使用。getStaticProps到第一个字节的时间(TTFB)将比慢,因为服务器必须计算每个请求的结果,并且如果没有额外的配置,CDN就无法缓存结果。
- 返回一个对象
- context包含:
- params: 使用动态路由页面的路由参数
- req: http 传入信息对象,以及其它内置的解析助手
- res: http返回对象
- query: 查询字符串的对象
- preview:是否处于预览模式。true/undefined
- previewData:setPreviewData设置的预览数据集
- resolvedUrl: 请求URL的规范化版本,去掉客户端转换的前缀并包括原始查询值
- locale:活动区域设置(前提:启用国际路由)
- locales:所有支持的区域设置(前提:启用国际路由)
- defaultLocale:配置的默认区域设置(前提:启用国际路由)
- props:可选对象,包含页面组件将接受的props
- notFound:可选的布尔值,允许页面返回404和页面
- redirect:可选的重定向值,允许重定向到内部和外部资源,构建时不允许重定向
- 在getServerSideProps中提供req中间件:req
- req.cookies: 一个包含请求发送的cookie的对象。默认为{}
1
2
3
4
5
6
7
8
9
10
11
12//js
export async function getServerSideProps(context) {
return {
props: {}
}
}
// ts
import { GetServerSideProps } from 'next'
export const getServerSideProps: GetServerSideProps = async (context) => {
// ...
}
- req.cookies: 一个包含请求发送的cookie的对象。默认为{}
在客户端获取数据
- 使用场景:如果页面包含频繁更新的数据,并且不需要预呈现数据。用户面板页面。
- 首先,立即显示没有数据的页面。页面的部分内容可以使用静态生成进行预渲染。您可以显示丢失数据的加载状态。然后,在客户端获取数据,并在准备好后显示。
SWR
- next团队创建的React数据获取挂钩
- 处理缓存、重新验证、焦点跟踪、间隔重新蚀刻等
1
2
3
4
5
6
7
8import useSWR from 'swr'
const fetcher = (url) => fetch(url).then((res) => res.json())
function Profile() {
const { data, error } = useSWR('/api/user', fetcher)
if (error) return <div>failed to load</div>
if (!data) return <div>loading...</div>
return <div>hello {data.name}!</div>
}
内置对 CSS 的支持
- 允许在js文件import css文件
添加全局样式表
- 创建一个
pages/_app.js
文件,然后import
css文件
- 创建一个
从
node_modules
目录import样式- 可以在应用程序的任何位置导入
- 全局样式表还是要在
pages/_app.js
导入 - 其它的对应组件页面导入
添加组件级CSS
- CSS模块支持的文件命名格式:
[name].module.css
- CSS模块时一项可选功能,仅对带有
.module.css
扩展名的文件启用
- CSS模块支持的文件命名格式:
对Sass的支持
- 文件后缀必须为:
.module.scss
或.module.sass
- 记得安装sass:npm i sass
- 自定义Sass参数:使用
next.config.js
文件中的sassOptions
属性进行配置 - Sass变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14// xxx.module.scss
$primary-color: #64FF00
:export {
primaryColor: $primary-color
}
// pages/_app.js
import variables from "../xxx.module.scss"
export default function MyApp({Component, pageProps}) {
return (
<Layout color={variables.primaryColor}>
<Component {...pageProps} />
</Layout>
)
}
- 文件后缀必须为:
- CSS-in-JS
- 内联样式:
<p style={{ color: 'red' }}>内联</p>
- 引入styled-jsx以支持作用于隔离的css,支持类似于Web组件的shadow css单不支持服务端渲染且仅支持js
- 内联样式:
布局
自定义单个共享布局:自己封装想要的布局组件然后应用在全局中
多个布局 | 嵌套布局:自己定义多个布局组件,然后使用getLayout实现,
- 这种布局支持状态持久性,因为React组件书在页面转换之间保持,这个过程叫协调。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20// pages/index.js
import Layout from '../component/layout'
import NestedLayout from '../component/nested-layout'
// ts引入import type { ReactElement } from 'react'
export default function Page() {
return {/*...*/}
}
// ts模式给page添加类型ReactElement
Page.getLayout = function getLayout(page) {
return (<Layout>
<NestedLayout>{page}</NestedLayout>
</Layout>)
}
// page/_app.js
// ts模式下需要定义布局类型
export default function MyApp({Component, pageProps}) {
const getLayout = Component.getLayout || (page => page)
return getLayout(<Component {...pageProps} />)
}
- 这种布局支持状态持久性,因为React组件书在页面转换之间保持,这个过程叫协调。
图片优化
- Image组件
next/image
是HTML元素<img/>
的扩展 - 使用图像组件:import Image from ‘next/image’,
<Image src={} />
本地图像
- 要先引入,使用import,next将根据导入的文件自动确定图像的width和height
远程映像:需要提供src、width、height、alt
域名:
- 访问远程映像单仍使用内置的next Image Optimization API,则src保留为默认设置
1
2
3
4
5
6
7// next.config.js
module.exports = {
images: {
// 这个地址是远程映像的图片的绝对地址
domains: ['examplae.com', 'example2.com']
}
}
优先权:’priority’添加到图像中,成为每个页面的最大内容绘制(LCP)元素
- LCP 元素通常是页面视口中可见的最大图像或文本块
图像大小调整
- 自动: 使用静态导入
- 显式地:通过包含
height
和width
- 隐式地:通过使用layout=”fill”,使图像展开填充父元素
不知道图像大小的情况下调整
- 使用
layout="fill"
,使用fill、contain或cover的objectFit属性以及objectPosition属性来定义 - 规范化图像:自己做的图,自己规范图像大小
- 修改API调用
- 如果以上都不适用,则将按照
<img />
元素的标准在页面上运行
- 使用
样式
- 建议对内部
<img>
设置样式的方法是将Image组件上的className设置为导入的CSS模块的值。className的值将自动应用于基础的<img>
元素。 - 当使用layout=’fill’>时,父元素必须具有position:relative(必要的)
- 当使用layout=’responsive’时,父元素必须具有display:block(div的默认值,应另行指定)
字体优化
- 第一内容绘制(FCP)和最大内容绘制(LCP)
- 使用:import Head from ‘next/head’
- 要添加web字体,使用
<Head></Head>
包裹 - 如果不想要优化,在next.config.js中设置
optimizeFonts: false
脚本组件
- 脚本组件
next/script
是HTML元素<script>
的扩展 - 使用:import Script from ‘next/script’,
<Script src="第三方路径" strategy="策略属性" />
strategy:决定何时加载第三方脚本
- beforeInteractive:在页面交互之前加载
- afterInteractive:(默认):在页面变成交互式后立即加载
- lazyOnload:在空闲时间加载
beforeInteractive
- 从服务器注入到初始HTML中,并在执行自绑定JavaScript之前运行。
- 此策略应用于在页面交互之前需要获取和执行的任何关键脚本
afterInteractive
内联脚本
- 使用:将javascript放在{}中编写 或者在标签上使用
dangerouslySetInnerHTML
属性,但必须定义id属性 - 只能策略属性不为beforeInteractive时才能使用
加载后执行代码(onLoad)
- 策略属性不为lazyOnload,都可以在加载后在标签上使用onLoad属性来执行代码
基本特性
静态文件服务
- 将静态文件存放到根目录下的
public
目录中,并对外提供访问。public目录下的存放的静态文件对外访问路径yi/
为起始路径,如”/xxx.png” - 要确保pages/下的文件于静态文件无重名
快速刷新
- 尝试在两次编辑之间保留零部件的状态。只要不更改参数或Hook调用的顺序,useState和useRef就会保留以前的值。
- 具有依赖关系的钩子将始终在快速刷新期间更新。在进行快速刷新时,依赖项列表将被忽略。
- 即使是空依赖项数组的useEffect,在快速刷新期间仍会重新运行一次。
ESLint
- 安装:npm run lint
- 然后配置
路由 next version 13+
基础篇
- App 路由
- app目录和pages目录一起工作,也兼容pages页面的路由,但主要在app页面的路由
- App Router的优先级高于Pages Router。
- 默认情况下, app内的组件是React服务器组件。性能优化之一
- 路由片段
- 路径的每个文件夹代表一个路由片段。每个路线段都映射到网址路径中相应的片段。
- 嵌套路由: 就是文件夹相互嵌套
- 主机托管
- app目录中可以放自己的文件和特殊文件等,因为只有page.js或route.js返回的内容是可公开寻址的
- 特殊文件:.js、.jsx、.tsx文件扩展名可用于特殊文件
- 私有文件:文件夹名:_folderName,表示该目录下的文件退出路由
- 高级路由模式
- 修改
<head>
- 通过到处layout.js或page.js文件中的metadata对象或generateMetadata功能来定义元数据
链接和导航
- 导航的两种方式:
- 使用
<Link href="">
组件, Link来自"next/link"
, 动态可以使用模板字符串语法- 相当于vue的声明式导航
- usePathname:检查链接是否处于活动状态,用于className判断
- 滚动到id:在url链接添加#id
- 禁用滚动恢复:添加属性scroll={false},默认滚动
- 使用useRouter 钩子
- 相当于vue的编程式导航,
1
2
3import { useRouter, usePathname } from "next/navigation"
const router = useRouter()
router.push('/dashboard', { scroll: false })
- 相当于vue的编程式导航,
- 使用
- 工作原理
- 预请求: 用户访问路由之前在后台预加载路由的方法
<Link>
组件:自动预取。静态prefetch默认为true,动态prefetch默认自动,只有共享布局向下直到为30s
,预取并缓存第一个loading.js文件。可以显示即时加载状态。router.prefetch()
:useRouter
钩子编程式预取路由- 预取在开发中不启用,仅在生产中启用
- 缓存:Next有一个内存中客户端缓存,称为路由缓存。 当用户在app中导航时,prefetched路线段和访问过的路线的React Server组件有效负载将存储在缓存中。
- 部分渲染:仅在客户端上重新渲染导航时发生变化的路线段,并且保留所有共享段。就是在同一文件夹下的不同文件不同路径,切换只渲染对应文件而不影响整体的文件
- 软导航:仅渲染已更改的片段,同时保留 React 和浏览器状态,并且不会重新加载整个页面。默认,浏览器执行硬导航
- 后退和前进导航
- 默认,将保持向后和向前导航的滚动位置,并重用路由缓存中的路线段。
- 预请求: 用户访问路由之前在后台预加载路由的方法
动态路由
- 在文件名称括上方括号([folderName])
- 动态端作为params属性将传递给layout、page、route和generateMetadata函数
- 生成静态参数
- generateStaticParams在构建时预动态路线段至静态生成路由结合起来
- 优点:智能检索数据
- 捕获所有片段: 通过在方括号使用[…folderName]
- 可选的包罗万象的段:[[…folderName]],
- 与捕获所有片段的区别:如果带可选参数,则不带参数的路由也会被匹配
加载 UI 和流式传输
- 即时加载状态(后备UI)
- 在导航时立即显示
- 在文件加中创建loading.js,并嵌套在统一文件夹中的layout.js内。会自动将page.js文件和下面的所有子文件封装在
<Suspense>
边界中。 - 即使使用 以服务器为中心的路由,导航也是即时的。
- 使用 Suspense 的流式: 自己看文档
错误处理error.js 文件
- 自动创建 React 误差边界、wraps 嵌套子段或 page.js 组件。
- 从 error.js 文件导出的 React 组件用作 fallback 组件。
- 如果在错误边界内抛出错误,则错误为 contained,回退组件为 rendered。
- 当回退错误组件处于活动状态时,布局 above 错误边界 maintain 其状态和 remain 交互,并且错误组件可以显示从错误中恢复的功能。
- 从错误中恢复
- 重试
- 使用reset()函数
- 处理布局中的错误
- error.js边界不捕获同一段的layout.js或template.js组件中抛出的错误
- 处理特定布局或模板的错误:将error.js文件放置在布局的父段
- 处理根布局或模板的错误:使用global-error.js
- 处理根布局中的错误
- 根app/error.js边界不捕获根app/layout.js或app/template.js组件中抛出的错误
- 处理: 使用文娱根app目录中的global-error.js
- global-error.js错误边界封装entire应用,并且其后被组件在活动时替换根布局
- 即使定义了global-error.js,仍建议定义一个根error.js其后背组件将渲染within根布局
并行路由
- 允许同时或有条件地在同一布局中渲染一个或多个页面。允许根据条件渲染插槽
- 插槽:使用slots创建并行路由,按照@folder约定定义的,并作为props传递到同一级别的布局
- children属性是一个隐式插槽,不需要映射到文件夹。即app/page.js === app/@children/page.js
- 未匹配的路由 default.js
- 默认情况下槽内渲染的内容将于当前URL匹配
- 在插槽文件夹下在新建default.js作为后备渲染
- useSelectedLayoutSegment(s)
- 都接收 parallelRoutesKey,它允许你读取该槽内的活动航路段。
- 当用户导航到 URL 栏中的 @auth/login 或 /login 时,loginSegments 将等于字符串 “login”
拦截路由
- 允许从当前布局内应用的其他部分加载路由。
- 使用
(...)
来定义,在文件夹的名称哪里添加,类似于相对路径../,约定基于路由段(.)
: 匹配同一水平上的段(..)
: 匹配高一级的段(..)(..)
: 匹配上面两级的段(...)
: 匹配root app目录中的段
路由处理程序
- 使用 Web 请求 和 响应 API 为给定路由创建自定义请求处理程序
- 仅在 app 目录中可用,相当于 pages 目录中的 API 路由,
在app目录内的route.js|ts
文件中定义1
2export const dynamic = "force-dynamic"
export async function GET(request: Request){} - 支持的HTTP方法:GET、POST、PUT、PATCH、DELETE、HEAD 和 OPTIONS
- 扩展API:
NextRequest
和NextResponse
- 行为
- 缓存
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15export async function GET() {
const {searchParams} = new URL(request.url)// 将Request对象与GET方法一起使用时存在
const id = searchParams.get('id') // 将Request对象与GET方法一起使用时存在
const res = await fetch(`https://data.mogodb.api.com/product/${id}` {
methods: 'POST' // post方法的情况存在
headers: {
'Content-Type': 'application/json',
'API-Key': process.env.DATA_API_KEY
},
body: JSON.stringify({ time: new Data().toISOString() }) // post方法的情况存在
})
const data = await res.json(
return Response.json({data})
)
}
- 缓存
- 选择退出缓存
- 将Request对象与GET方法一起使用
- 使用任何其它HTTP方法
- 像使用cookies和headers一样使用动态函数
片段配置选项
手动指定动态模式
中间件
- 在缓存内容和路由匹配之前运行
- 使用项目根目录中的文件middleware.ts|js来定义中间件
数据获取
数据获取、缓存和重新验证
- 四种方式获取数据
- 在服务器上,使用fetch
- 在服务器上,带有第三方库
- 在客户端上,通过路由处理程序
- 在客户端,使用第三方库
- 在服务器上,使用fetch
- 可以在服务器组件、路由处理程序 和 服务器操作 中将 fetch 与 async/await 一起使用
- 在路由处理程序中,fetch 请求不会被记忆,因为路由处理程序不是 React 组件树的一部分。
- 缓存数据
- 默认情况下, 会自动将fetch的返回值缓存到服务器上的数据缓存
- 数据缓存是持久的HTTP缓存。根据你的平台,缓存可以自动扩展并达到跨多个区域共享
- 重新验证数据
- 是清除数据缓存并重新获取最新数据的过程。当数据变化时要确保显示最新数据时非常有用
- 两种方式:
- 基于时间的重新验证:定时更新。用于不经常更改且不重要的数据
- 使用fetch的next.revalidate选项来设置资源的缓存生命周期
fetch("heeps://...",{ next: { revalidate: 3600}})
- 按需重新验证:根据时间手动重新验证数据。
- 基于时间的重新验证:定时更新。用于不经常更改且不重要的数据
- 数据可以通过路径(revalidatePath)或通过服务器动作或路由处理程序内的缓存标签 (revalidateTag)按需重新验证
- next 有一个缓存标记系统,用于使跨路由的fetch请求无效
- 使用fetch时,可以选择使用一个或多个标签来标记缓存条目
- fetch(‘https://…’, {next: {tags: [‘collection’]}})
- 然后,调用revalidateTag来重新验证与改标签关联的条目
1
2import { revalidateTag } from 'next/cache'
export default async function action() { revalidateTag('collection')}
- 使用fetch时,可以选择使用一个或多个标签来标记缓存条目
- 选择退出数据缓存
- 满足以下条件,则 fetch 请求将被缓存 not
- cache: ‘no-store’ 添加到 fetch 请求中。个人
- revalidate: 0 选项添加到各个 fetch 请求中。
- fetch 请求位于使用 POST 方法的路由处理程序内部。
- fetch 请求在使用 headers 或 cookies 之后出现。
- 使用 const dynamic = ‘force-dynamic’ 航路段选项。
- fetchCache 路由段选项默认配置为跳过缓存。
- fetch 请求使用 Authorization 或 Cookie 标头,并且组件树中其上方有一个未缓存的请求。
数据获取模式
- 在服务器上获取数据
- 在需要的地方获取数据: 在在数据组件使用fetch或react cache。因为fetch会自动记忆
- 流式(Streaming)和 悬念(Suspense) :逐步渲染UI并将其增量流式传输到客户端
- 并行和顺序数据获取
- 使用顺序数据获取,路由中的请求相互依赖,因此会创建瀑布。当因为一次提取取决于另一次提取的结果,或者希望在下一次提取之前满足某个条件以节省资源的情况使用。类似于react从布局中单独拎其中一个布局渲染完在回到原布局。
- 使用并行数据获取,路由中的请求会立即发起,同时加载数据。就是两个函数同时请求完然后布局在调用
- 预加载数据
- 创建
preload
函数。不必将Promise作为props传递下去。preload函数是一种模式,名字可自定义1
2
3export const preload= (id: string) => {
void getItem(id) // 预加载的函数API
} - 然后在正常请求这个API(getItem),最后页面函数直接调用preload即可
- 创建
- 使用 React cache、server-only 和预加载模式
- 通过这种方法,可以与获取数据,缓存响应,并保证此数据获取只发生在服务器上
1
2
3
4
5import { cache } from 'react'
import 'server-only'
export const preload = (id: string) => { void getItem(id) }
export const getItem = cache(async(id: string) => {})
- 通过这种方法,可以与获取数据,缓存响应,并保证此数据获取只发生在服务器上
形式和突变
- 仅服务器表单
- 在函数顶部使用”use server”
- 重新验证数据
- 使用revalidatePath(‘/‘)使整个路线段失效或使用revalidateTag()使带有缓存标记的特定数据失效
- 重定向
- 使用redirect(url)
- 表单验证
- 使用required和type=”email”等HTML验证进行基本表单验证
- 显示加载状态
- 当表单在服务器上提交时,使用useFormStatus()显示加载状态,只能用作服务器操作的form元素的子元素
- 错误处理
- 请求时一起返回
- 乐观的更新
- 使用useOptimistic(messages,(state,newMessage) => […state, {message:newMessage}])在服务器操作完成之前乐观地更新 UI
- 设置cookie
- cookies().set(key,val)
- 读取cookie
- cookies().get(key)?.value
- 删除cookie
- cookies().delete(key)
渲染
服务器组件
- 三种不同的服务器渲染策略
- 静态渲染
- 动态渲染
- 流式
- 优点
- 数据获取:减少渲染所用时间及客户端发出的请求量
- 安全
- 缓存:可以重用
- 打包尺寸:对网速慢或设备差的用户有益
- 初始页面加载和首次内容绘制FCP:可以生成用户立即查看的页面
- 搜索引擎优化和社交网络共享性
- 流式:渲染分多块,按块渲染
服务器组件是如何渲染的?
- 渲染工作被分成几个块:按个别路线段和悬念边界,每个块都分两步渲染:
- React将服务器组件渲染为一种称为React服务器组件有效负载(RSC 有效负载)的特殊数据格式。
- Next.js 使用 RSC 有效负载和客户端组件 JavaScript 指令在服务器上呈现 HTML。
- 然后,在客户端:
- HTML用于立即显示路线的快速非交互式预览
- React服务器组件有效负载用于协调客户端和服务器组件树,并更新DOM
- JavaScript指令用于hydrate客户端组件并使应用具有交互性
- 静态渲染(默认)
- 路线在构建时间渲染或在数据重新验证之后在后台验证。结果被缓存并可以推送到内容分发网络CDN
- 适用:已知内容
- 动态渲染
- 在请求时间处为每个用户渲染路线
- 适用:个性化数据和仅在请求时才能直到的信息
客户端组件
- 在请求时在客户端上渲染交互式UI
- 优点:
- 互动性: 客户端组件可以适用状态、效果和事件监听器,意味着可以向用户踢狗即时反馈并更新UI
- 浏览器API:客户端组件可以访问浏览器API
- 使用: 在文件顶部添加
"use client"
,声明服务器和客户端组件模块之间的boundary - 客户端组件时如何渲染的?
- 整页加载
- 在服务器上:
- React 将服务器组件渲染为一种称为 React 服务器组件有效负载(RSC 有效负载) 的特殊数据格式,其中包括对客户端组件的引用。
- Next.js 使用 RSC 有效负载和客户端组件 JavaScript 指令在服务器上呈现路由的 HTML。
- 然后,在客户端:
- HTML 用于立即显示路线的快速非交互式初始预览。
- React 服务器组件有效负载用于协调客户端和服务器组件树,并更新 DOM。
- JavaScript 指令用于 hydrate 客户端组件并使其 UI 具有交互性。
- 在服务器上:
- 后续导航
- 在后续导航中,客户端组件完全在客户端上渲染,而不需要服务器渲染的 HTML。
- 整页加载
- 这意味着客户端组件 JavaScript 包已下载并解析。 一旦包准备好,React 将使用 RSC Payload 来协调客户端和服务器组件树,并更新 DOM。
服务器和客户端组合模式
- 服务器组件模式
- 组件之间共享数据
- 将仅服务器代码排除在客户端环境之外
- 使用第三方软件包和提供商
- 客户端组件
- 将客户端组件移至树下
- 将属性从服务器传递到客户端组件(序列化)
- 交错服务器和客户端组件
- 不支持的模式:将服务器组件导入客户端组件
- 支持的模式:将服务器组件作为 Props 传递给客户端组件
- 使用 React children 属性在客户端组件中创建 “slot”。
Edge 和 Node.js 运行时
- Node.js 运行时(默认)可以访问生态系统中的所有 Node.js API 和兼容包。
- Edge 运行时 是基于 网络 API 的。