Next.js
bash
npx create-next-app@latest
服务器组件
- 服务器组件
- 服务器组件就是只在服务器端运行的 React 组件,它完全在服务器端渲染,渲染结果会随着 SSR 渲染的 HTML 传递给客户端(即浏览器端)。
- 并不是只有async 异步函数才能定义服务器组件。异步函数并不是服务器组件的特征,而是一个特性。
- 服务器组件本身不会在浏览器端渲染(或重新渲染),也不会在浏览器端进行水合,甚至服务器组件的源代码都不会被打包进浏览器加载的 JS 中
- 服务器组件可以跟客户端组件,也就是传统 React 组件配合使用
- 你可以把服务器组件的子组件抽取到独立的文件中,在文件顶部加入'use client'指示符,这个文件所包含的组件就会被视为客户端组件,在这里你可以放心地使用各种 Hooks、事件处理函数,以及其他浏览器端的 JS 交互代码。
- 服务器组件与客户端组件之间可以通过 props 通信。
- 服务器组件传递给客户端组件的 props 必须是可序列化(serializable)的数据类型,或者是 Promise(请回忆一下上节课的 use(Promise) ),甚至可以传递服务器 action 的函数引用。
- 但因为服务器组件并不包含状态,使得它与客户端组件之间的通信基本是单向的。
- 如果你希望通过客户端组件的交互影响服务器组件,通常需要借助路由。
- 服务端组件子组件
- 服务器组件的子组件可以是服务器组件也可以是客户端组件。
- 客户端组件子组件
- 在客户端组件所在文件中,用 import 语句导入的子组件、后代组件也都会自动被视为客户端组件。
- 服务器组件传递给客户端组件的 props 除了前面列举的类型,还可以传递 React 元素(即 JSX),这里的元素并不限定是客户端组件还是服务器组件的元素
- 这就带来了一种灵活的组件混合模式:通过 children prop,将服务器组件传递给客户端组件。
jsximport ClientComponent from './ClientComponent.jsx'; import ServerComponent from './ServerComponent.jsx'; // Page默认为服务器组件 export default function Page() { return ( <ClientComponent> <ServerComponent /> </ClientComponent> ); }
服务器 Action
能被客户端组件调用的、在服务器端执行的 action。 如果表单 action 能直接在服务器端执行,那就可以省略服务器端 API,直接将表单数据保存到数据库里!
contact
jsx
'use client';
// 运行在客户端(即浏览器端)的
import { useState } from 'react';
export default function ContactForm() {
const [name, setName] = useState();
const formAction = async (formData) => {
const contactName = formData.get('name');
await new Promise((resolve) => setTimeout(resolve, 1000));
setName(contactName);
};
return (
<>
<h2>contact</h2>
<form action={formAction}>
<input type="text" name="name" placeholder="联系人名称" />
<button type="submit">提交</button>
{name && <p>您提交的联系人名称是:{name}</p>}
</form>
</>
);
}
contactAction
jsx
'use client';
// 除了在客户端组件 import 导入服务器 action,
// 也可以由服务器组件通过 props 将服务器 action 的引用传递给客户端组件。
import { saveContactAction } from './actions.jsx';
import { useActionState } from 'react';
export default function ContactForm() {
const [_state, formAction, pending] = useActionState(
async (_currentState, formData) => {
await saveContactAction(formData)
}, {});
return (
<>
<h2>contactAction</h2>
{/* saveContactAction会在服务器端被执行 */}
{/* 其实这个过程还是存在浏览器端对服务器端 API 的调用的,只不过 Next.js 框架为你代劳了。 */}
<form action={formAction}>
<input type="text" name="name" placeholder="联系人名称" />
<button type="submit">提交</button>
{pending && <p>提交中...</p>}
</form>
</>
);
}
jsx
'use server';
import { redirect } from 'next/navigation';
import fs from 'node:fs/promises';
// saveContactAction的服务器 action
export async function saveContactAction(formData) {
// 'use server';
await new Promise((resolve) => setTimeout(resolve, 5000));
const content = await fs.readFile('./data.json', 'utf-8');
const { contacts } = JSON.parse(content);
contacts.push({
id: contacts.length + 1,
name: formData.get('name'),
});
const newContent = JSON.stringify({ contacts }, null, 4);
await fs.writeFile('./data.json', newContent, 'utf-8');
redirect('/');
}
pagination
jsx
import { getDataByPage } from './actions';
import PaginationChild from './paginationChild.jsx';
export default async function A () {
const {data,total} = await getDataByPage(1)
return <div>
<h2>Pagination</h2>
<PaginationChild initData={data} initTotal={total}></PaginationChild>
</div>;
}
jsx
'use client';
import { startTransition, useState } from 'react';
import { getDataByPage } from './actions.jsx';
export default function PaginationChild ({initData,initTotal}) {
const [data, setData] = useState(initData);
const [total, setTotal] = useState(initTotal);
const getPage = (page) => {
startTransition(async() => {
const {data,total} = await getDataByPage(page)
setData(data);
setTotal(total);
});
}
return <div>
<ul> data:{data.map((item) => <li key={item}>{item}</li>)}</ul>
<span>total: {total}</span>
<div>
<button onClick={() => getPage(1)}>第1页</button>
<button onClick={() => getPage(2)}>第2页</button>
</div>
</div>;
}
jsx
'use server';
export async function getDataByPage(page) {
// 模仿数据库
await new Promise((resolve) => setTimeout(resolve, 1000));
if(page === 1){
return {
data: ["id 1", "id 2", "id 3", "id 4", "id 5", "id 6", "id 7", "id 8", "id 9", "id 10"],
total: 100,
};
}else{
return {
data: ["id 11", "id 12", "id 13", "id 14", "id 15", "id 16", "id 17", "id 18", "id 19", "id 20"],
total: 100,
};
}
}
turbopack
Next.js 新一代的打包器
- 特点
- 开发环境更快 - 项目启动时更快,代码改动后的页面更新几乎是瞬间反应,提升开发反馈速度。
- 增量编译 - 不用重新打包全部代码,只打包改动部分,节约时间和系统资源。
- 多环境支持更好 - 可以同时高效地处理浏览器端和服务器端代码,适合 Next.js 的多端渲染特性。
- 内置对 React Server Components 和 TypeScript 的优化支持,更适合现代 React 项目。
- 多核并行处理 - 利用多核计算机优势,处理速度更快。
- 参考