Next.js 学习日记
Next.js 服务端组件允许直接在组件中使用 async/await 获取数据,无需传统前端状态管理。作为 Vue 开发者,你会惊讶于这种简洁性:只需在服务端组件中直接调用数据库或 API,Next.js 会自动处理数据获取和渲染。这种模式消除了客户端数据请求的繁琐流程(useEffect/onMounted、loading状态等),显著提升开发效率。关键区别在于服务端组件在构建/请求时执行数据
一、next.js 基础
1. 路由:文件即路径 (App Router)
在 Next.js 最新的 App Router 模式下,你不再需要手动配置路由表。项目中的 app 目录决定了你的 URL 结构。
- 页面文件:必须命名为
page.jsx。 - 布局文件:命名为
layout.jsx(相当于 Vue 的App.vue或嵌套布局中的外壳)。
目录结构示例:
app/page.jsx➡️ 对应/(首页)app/about/page.jsx➡️ 对应/aboutapp/blog/[slug]/page.jsx➡️ 对应/blog/:slug(动态路由,类似 Vue 的:id)
2. 核心区别:服务端组件 (Server) vs. 客户端组件 (Client)
这是 Next.js 最基础也最重要的概念。在 Next.js 中,组件默认都是服务端组件 (RSC)。
| 特性 | 服务端组件 (Server Component) | 客户端组件 (Client Component) |
|---|---|---|
| 运行位置 | 仅在服务器运行 | 在浏览器和服务器运行 |
| 交互性 | 不能使用 Hooks (useState, useEffect) 或点击事件 |
可以使用所有 React 特性 |
| 数据获取 | 直接 async/await 获取数据库或 API |
传统 useEffect 方式 |
| 性能 | 零 JS 发送到浏览器,加载飞快 | 会发送 JS 代码包 |
Vue 开发者注意:如果你需要在组件里用 useState 或者监听 onClick,必须在文件的第一行加上:
'use client';
import { useState } from 'react';
// ... 剩下的逻辑
3. 布局系统 (Shared Layouts)
Next.js 允许你定义嵌套布局。例如,你希望所有“后台管理”页面都有相同的侧边栏,而不需要在每个页面都引入。
// app/dashboard/layout.jsx
export default function DashboardLayout({ children }) {
return (
<section>
<nav>侧边栏</nav>
{children} {/* 这里的 children 对应具体的页面内容 */}
</section>
);
}
4. 渲染模式:三种武器
Next.js 强大的原因在于它允许你针对每个页面选择最佳的渲染策略:
- SSR (Server Side Rendering):每次用户请求时,在服务器上实时渲染 HTML。数据永远是最新的。
- SSG (Static Site Generation):在项目编译时就生成好 HTML。适合博客、文档,访问速度极快。
- ISR (Incremental Static Regeneration):黑科技。在项目运行过程中,每隔一段时间自动更新静态页面,兼顾速度和实时性。
5. 内置的优化组件
Next.js 帮你把最麻烦的性能优化做好了:
<Image />:自动压缩图片、自动支持 WebP、自动懒加载(防止图片导致的布局抖动)。<Link />:自动预加载目标页面的代码。当用户鼠标悬停在链接上时,Next.js 已经偷偷把下个页面的数据下载好了,点击瞬间切换(类似 SPA 的体验)。
老师的建议:开启你的第一个项目
不要只看文档,现在就开始:
- 运行
npx create-next-app@latest。 - 在安装选项中,务必选择:
App Router: Yes(这是目前的标准)Tailwind CSS: Yes(React 圈子最推荐的样式方案)
- 尝试在
app下创建一个about文件夹,并写一个page.jsx。
你想现在看看如何用 Next.js 优雅地获取服务器数据(直接写 async/await),还是想先弄清楚如何在 Next.js 里处理 CSS 样式?
二、路由系统
Next.js 的路由系统是它最核心的特性之一。作为 Vue 开发者,你可能习惯了在 router/index.js 中配置一个巨大的路由表,但在 Next.js 中,“目录结构即路由”。
目前 Next.js 推荐使用 App Router(即 app/ 目录),它是基于文件系统的路由系统。
1. 基础路由 (Basic Routes)
在 app 文件夹下,文件夹的名字决定了 URL 路径,而该文件夹下的 page.jsx 文件则是该路径渲染的 UI 内容。
- 根路由:
app/page.jsx➡️/ - 嵌套路由:
app/dashboard/page.jsx➡️/dashboard - 深层嵌套:
app/dashboard/settings/page.jsx➡️/dashboard/settings
注意:文件夹里可以放其他文件(如
utils.js或Button.jsx),但只有名为page.jsx的文件才能被公开访问。这和 Vue 组件文件夹的组织方式很像。
2. 动态路由 (Dynamic Routes)
当你需要根据 ID 或 Slug 加载页面时(类似于 Vue 的 /user/:id),你可以使用 方括号 命名文件夹。
and [slug]]
- 目录结构:
app/blog/[id]/page.jsx - 对应 URL:
/blog/123或/blog/hello-world - 如何获取参数:
Next.js 会自动将参数注入到page组件的params属性中。
// app/blog/[id]/page.jsx
export default function BlogPost({ params }) {
// 假设访问 /blog/123,params.id 就是 "123"
return <h1>正在阅读文章:{params.id}</h1>;
}
3. 布局系统 (Layouts & Templates)
这是 Next.js 路由最强大的地方:持久化布局。
根布局 (Root Layout)
app/layout.jsx 是必须的。它相当于 Vue 的 App.vue,包含了 <html> 和 <body> 标签。它在整个应用中是持久化的,页面切换时不会重新渲染。
嵌套布局 (Nested Layouts)
如果你想让 /dashboard 下的所有页面都有一个侧边栏,但 /login 页面没有,你可以在 app/dashboard/ 下创建一个私有的 layout.jsx。
// app/dashboard/layout.jsx
export default function DashboardLayout({ children }) {
return (
<div className="flex">
<aside>侧边栏</aside>
<main>{children}</main> {/* 这里的 children 就是 dashboard 目录下的各种 page */}
</div>
);
}
4. 特殊文件 (Special Files)
除了 page.jsx 和 layout.jsx,Next.js 路由系统还预定义了一些特殊文件,帮你处理各种边界情况:
| 文件名 | 作用 | 对应 Vue 概念 |
|---|---|---|
loading.jsx |
定义该路径下的加载中状态(自动使用 React Suspense) | 自定义 Loading 组件 |
error.jsx |
定义该路径下的错误处理界面(捕获运行时错误) | Error Boundary |
not-found.jsx |
当该路径下发生 404 时显示的页面 | 404 路由 |
例如,如果你在 app/dashboard 下放了一个 loading.jsx,当你从首页点击进入仪表盘时,Next.js 会自动在数据准备好之前显示这个 loading 内容,无需你手动控制 isLoading 状态。
5. 导航 (Navigation)
① 组件导航:<Link>
不要使用原生的 <a> 标签,因为它会引起页面刷新。使用 Next.js 的 <Link> 组件,它支持预获取 (Prefetching),让跳转像秒开一样。
import Link from 'next/link'
<Link href="/dashboard">去后台</Link>
② 编程式导航:useRouter
如果你要在逻辑中跳转(比如点击登录按钮后),使用 useRouter 钩子。
重要提示:在 App Router 中,必须从
'next/navigation'导入,而不是'next/router'。
'use client'; // 记住:有交互逻辑的组件要加这一行
import { useRouter } from 'next/navigation';
function LoginPage() {
const router = useRouter();
return (
<button onClick={() => router.push('/home')}>登录</button>
);
}
总结:Next.js 路由的心智模型
- 文件夹 = URL 路径。
- page.jsx = 页面内容。
- layout.jsx = 共享的外壳(不会随页面切换而刷新)。
- [] = 动态参数。
你现在已经了解了如何“走位”。接下来,想不想看看 Next.js 的杀手锏——如何直接在“服务器组件”里用 async/await 拿数据?(这能让你少写 80% 的请求逻辑)
三、如何直接在“服务器组件”里用 async/await 拿数据
这是 Next.js 最令人兴奋的地方!在 Vue3 或纯 React 中,拿数据通常需要:ref/state + onMounted/useEffect + axios/fetch + 处理 loading 状态。
在 Next.js 的 服务端组件 (Server Components) 中,你可以像写后端代码一样,直接把组件写成 async 函数,然后用 await 拿数据。
1. 基础用法:组件即数据请求器
在 app 目录下的文件默认都是服务端组件。你不需要任何 Hook,直接在函数体里 await 即可。
// app/users/page.jsx
// 1. 将组件定义为 async 函数
export default async function UsersPage() {
// 2. 直接请求数据(这是在服务器上运行的,不会暴露 API Key 给客户端)
const res = await fetch('https://api.example.com/users');
const users = await res.json();
// 3. 直接渲染数据
return (
<main>
<h1>用户列表</h1>
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</main>
);
}
2. 为什么这样做很“香”?
- 没有“水合”抖动:数据在 HTML 发送到浏览器之前就已经填好了,用户看到的就是最终内容,SEO 极佳。
- 直接访问数据库:因为代码在服务器运行,你可以直接在组件里写数据库查询(如
db.query()),连 API 接口都可以省了。 - 安全性:敏感的 Token 或秘钥只留在服务器,不会传给浏览器。
- 减小包体积:用于处理数据的第三方库(比如
moment.js或lodash)不会被下载到用户的浏览器里。
3. 如何处理 Loading 状态?
你可能会问:“如果是异步请求,页面加载时不是会白屏吗?”
Next.js 使用了 Streaming(流式传输)。你只需要在同级目录下创建一个 loading.jsx。
// app/users/loading.jsx
export default function Loading() {
return <p>正在加载数据,请稍候...</p>;
}
当 page.jsx 在请求数据时,Next.js 会自动先给用户展示 loading.jsx 的内容,等数据请求完了再自动替换成 page.jsx。你不需要写任何判断 if (isLoading) 的逻辑。
4. 数据缓存与更新 (Revalidation)
Next.js 的 fetch 是加强版的。它默认会缓存数据。如果你希望数据是动态的,可以设置:
// 每 60 秒刷新一次缓存 (类似 ISR)
const res = await fetch('...', { next: { revalidate: 60 } });
// 或者是完全不缓存,每次请求都重新获取 (类似传统的 SSR)
const res = await fetch('...', { cache: 'no-store' });
5. 注意事项 (Vue 开发者易错点)
- 不能在
async组件里用 Hook:如果你把组件写成了async,你就不能在里面用useState或useEffect。如果需要交互,请将交互部分抽离成一个带'use client'的子组件。 - 只能在服务端组件用:
async组件这种写法不适用于'use client'的客户端组件。客户端组件拿数据依然建议用传统的useEffect或更好的SWR / React Query。
老师的总结:
- Vue 思路:组件挂载 -> 发送请求 -> 更新响应式变量 -> 重新渲染。
- Next.js 思路:服务器请求数据 -> 渲染完整 HTML -> 发送给浏览器。
这种**“全栈组件”**的开发模式极大简化了数据流。
既然你已经学会了如何在服务端“拿”数据,那如果你想在页面上做一个按钮点击后“修改”数据(比如提交表单),该怎么办呢?这涉及到了 Next.js 的另一个黑科技:Server Actions。想听听吗?
四、
既然你已经学会了在服务端“读”数据,那么“写”数据(比如提交表单、修改数据库)就是全栈开发的最后一块拼图。
在传统的 React/Vue 开发中,你需要写一个 API 接口,然后在前端用 axios.post 去调用。在 Next.js 中,我们有更优雅的选择:Server Actions。
四、Server Actions
1. 什么是 Server Actions?
Server Actions 允许你直接在组件里定义一个异步函数,并将其标记为 "use server"。当你调用这个函数时,Next.js 会自动帮你完成所有的网络请求封装。
心智模型:你就像在调用一个普通的本地函数,但它实际上是在服务器上执行的。
2. 基础用法:提交表单
这是最经典的用法。你不需要 useState 来绑定每一个输入框,直接利用 HTML 原生的 formData。
// app/posts/create/page.jsx
export default function CreatePost() {
// 1. 定义一个 Server Action
async function createPost(formData) {
'use server'; // 关键:告诉 Next.js 这是要在服务端运行的代码
const title = formData.get('title');
const content = formData.get('content');
// 2. 这里可以直接操作数据库(比如用 Prisma 或 SQL)
console.log(`正在保存文章:${title}`);
// 3. 处理完后,可以跳转或刷新页面
}
return (
<form action={createPost}>
<input name="title" placeholder="标题" />
<textarea name="content" placeholder="内容" />
<button type="submit">发布文章</button>
</form>
);
}
3. 核心优势:为什么不用传统的 API 请求?
- 类型安全:如果你使用 TypeScript,Server Actions 可以完美共享类型定义。
- 减少代码量:你不需要写 API Route,不需要处理
fetch('/api/...')的繁琐过程。 - 渐进增强:即使用户的浏览器禁用了 JavaScript,这种基于
form action的提交依然有效(这是 Next.js 追求的极致体验)。
4. 修改数据后的“页面刷新” (Revalidation)
在 Vue 中,你修改了数据后,通常需要手动重新请求一次列表。在 Next.js 中,你只需要一行代码:revalidatePath。
import { revalidatePath } from 'next/cache';
import { redirect } from 'next/navigation';
async function createPost(formData) {
'use server';
// ... 保存数据的逻辑 ...
// 告诉 Next.js:"/posts" 路径下的缓存过期了,请重新获取数据
revalidatePath('/posts');
// 跳转回列表页
redirect('/posts');
}
5. 处理 Pending 状态 (正在提交…)
既然是异步操作,用户点击按钮后总需要一个反馈。React 提供了新的 Hook useFormStatus 来处理这个。
注意:因为它涉及交互,这个 Hook 必须在
'use client'组件中使用。
'use client';
import { useFormStatus } from 'react-dom';
function SubmitButton() {
const { pending } = useFormStatus();
return (
<button disabled={pending}>
{pending ? '正在发布...' : '发布文章'}
</button>
);
}
老师的最后总结
你现在的 React/Next.js 技能树已经非常完整了:
- 基础 React:JSX, Props, State, Hooks。
- Next.js 路由:文件即路由,嵌套布局。
- 数据读取:服务端组件直接
await fetch。 - 数据写入:Server Actions 直接操作后端逻辑。
这就是目前最先进的 “全栈 React” 开发范式。
恭喜你,React 老师的正式课程告一段落!你想让我最后为你推荐一些“React 生态中的明星库”(比如 UI 库、状态管理、动画库),作为你接下来的自学地图吗?
更多推荐



所有评论(0)