ChenZhen 搜索
首页 标签 归档 留言板 友链 ChatGPT 提示库 AI工具导航网 🚇开往 关于我

Next.js 学习日记

Next.js 服务端组件允许直接在组件中使用 async/await 获取数据,无需传统前端状态管理。作为 Vue 开发者,你会惊讶于这种简洁性:只需在服务端组件中直接调用数据库或 API,Next.js 会自动处理数据获取和渲染。这种模式消除了客户端数据请求的繁琐流程(useEffect/onMounted、loading状态等),显著提升开发效率。关键区别在于服务端组件在构建/请求时执行数据获取,结果直接嵌入生成的 HTML 中。要使用此特性,只需将组件声明为 async 并直接调用数据源,Next.

ChenZhen 2026-03-01T15:36:39

Next.js 学习日记

一、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 ➡️ 对应 /about
  • app/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 强大的原因在于它允许你针对每个页面选择最佳的渲染策略:

  1. SSR (Server Side Rendering):每次用户请求时,在服务器上实时渲染 HTML。数据永远是最新的。
  2. SSG (Static Site Generation):在项目编译时就生成好 HTML。适合博客、文档,访问速度极快。
  3. ISR (Incremental Static Regeneration)黑科技。在项目运行过程中,每隔一段时间自动更新静态页面,兼顾速度和实时性。

5. 内置的优化组件

Next.js 帮你把最麻烦的性能优化做好了:

  • <Image />:自动压缩图片、自动支持 WebP、自动懒加载(防止图片导致的布局抖动)。
  • <Link />:自动预加载目标页面的代码。当用户鼠标悬停在链接上时,Next.js 已经偷偷把下个页面的数据下载好了,点击瞬间切换(类似 SPA 的体验)。

老师的建议:开启你的第一个项目

不要只看文档,现在就开始:

  1. 运行 npx create-next-app@latest
  2. 在安装选项中,务必选择:
  • App Router: Yes (这是目前的标准)
  • Tailwind CSS: Yes (React 圈子最推荐的样式方案)
  1. 尝试在 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.jsButton.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.jsxlayout.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)

不要使用原生的 <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.jslodash)不会被下载到用户的浏览器里。

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 开发者易错点)

  1. 不能在 async 组件里用 Hook:如果你把组件写成了 async,你就不能在里面用 useStateuseEffect。如果需要交互,请将交互部分抽离成一个带 'use client' 的子组件。
  2. 只能在服务端组件用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 技能树已经非常完整了:

  1. 基础 React:JSX, Props, State, Hooks。
  2. Next.js 路由:文件即路由,嵌套布局。
  3. 数据读取:服务端组件直接 await fetch
  4. 数据写入:Server Actions 直接操作后端逻辑。

这就是目前最先进的 “全栈 React” 开发范式。

恭喜你,React 老师的正式课程告一段落!你想让我最后为你推荐一些“React 生态中的明星库”(比如 UI 库、状态管理、动画库),作为你接下来的自学地图吗?

© 版权声明
😀😃😄😁😆😅🤣😂🙂🙃😉😊😇🥰😍🤩😘😗😚😙😋😛😜🤪😝🤑🤗🤭🤫🤔🤐🤨😐😑😶😏😒🙄😬🤥😌😔😪🤤😴😷🤒🤕🤢🤮🤧🥵🥶🥴😵🤯🤠🥳😎🤓🧐😕😟🙁☹️😮😯😲😳🥺😦😧😨😰😥😢😭😱😖😣😞😓😩😫🥱😤😡😠🤬