npx create-next-app@latest从Vue迁移的最佳实践

Next.js 13+ 核心开发指南:从Vue迁移的最佳实践

  1. 路由系统:基于文件系统的App Router
  • 目录结构即路由(app/about/page.jsx → /about)
  • 动态路由使用[slug]文件夹命名
  • 布局系统通过layout.jsx实现共享UI(类似Vue的App.vue)
  1. 数据获取革命
  • 服务端组件直接使用async/await获取数据
  • 无需useEffect/onMounted,数据预渲染提升SEO
  • 自动处理Loading状态(loading.jsx)和缓存控制
  1. Server Actions新范式
  • 表单提交无需API路由,直接定义服务端函数
  • 支持渐进增强,即使JS禁用也能工作
  • 内置revalidatePath实现数据更新后自动刷新
  1. 关键差异点
  • 相比Vue的响应式数据流,Next.js采用服务端优先渲染
  • 交互逻辑需通过"use client"标记的客户端组件处理
  • 路由导航使用Next.js专属的<Link>和useRouter

本文完整呈现了从Vue转向Next.js的核心概念转变和技术栈差异,帮助开发者快速掌握现代React全栈开发模式。

创建项目

运行 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 的路由系统是它最核心的特性之一。作为 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

javascript 复制代码
// 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 状态。

|---------------------|-----------------------------------|----------------|
| 文件名 | 作用 | 对应 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'

javascript 复制代码
'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 = 共享的外壳(不会随页面切换而刷新)。
  • [] = 动态参数。

如何直接在"服务器组件"里用 async/await 拿数据?

在 Vue3 或纯 React 中,拿数据通常需要:ref/state + onMounted/useEffect + axios/fetch + 处理 loading 状态。

在 Next.js 的 服务端组件 (Server Components) 中,你可以像写后端代码一样,直接把组件写成 async 函数,然后用 await 拿数据。
------------------------------------------------

1. 基础用法:组件即数据请求器

app 目录下的文件默认都是服务端组件。你不需要任何 Hook,直接在函数体里 await 即可。

javascript 复制代码
// 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. 为什么这样做很"香"?

没有"水合"抖动SEO友好 :数据在 HTML 发送到浏览器之前就已经填好了,用户看到的就是最终内容,SEO 极佳。
直接访问数据库 :因为代码在服务器运行,你可以直接在组件里写数据库查询(如 db.query()),连 API 接口都可以省了。
安全性 :敏感的 Token 或秘钥只留在服务器,不会传给浏览器。
减小包体积:用于处理数据的第三方库(比如 moment.js 或 lodash)不会被下载到用户的浏览器里。

3. 如何处理 Loading 状态?

你可能会问:"如果是异步请求,页面加载时不是会白屏吗?"

Next.js 使用了 Streaming(流式传输) 。你只需要在同级目录下创建一个 loading.jsx

javascript 复制代码
// 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 是加强版的。它默认会缓存数据。如果你希望数据是动态的,可以设置:

javascript 复制代码
// 每 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 -> 发送给浏览器。

Server Actions

写"数据(比如提交表单、修改数据库)

在传统的 React/Vue 开发中,你需要写一个 API 接口,然后在前端用 axios.post 去调用。在 Next.js 中,我们有更优雅的选择:Server Actions

1. 什么是 Server Actions?

Server Actions 允许你直接在组件里定义一个异步函数,并将其标记为 "use server"。当你调用这个函数时,Next.js 会自动帮你完成所有的网络请求封装。

心智模型:你就像在调用一个普通的本地函数,但它实际上是在服务器上执行的。

2. 基础用法:提交表单

这是最经典的用法。你不需要 useState 来绑定每一个输入框,直接利用 HTML 原生的 formData

javascript 复制代码
// 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

javascript 复制代码
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' 组件中使用。

javascript 复制代码
'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 直接操作后端逻辑。
相关推荐
工程师老罗2 小时前
LVGL文本显示问题,编码问题
java·开发语言
宵时待雨2 小时前
C++笔记归纳15:红黑树
开发语言·数据结构·c++·笔记
四千岁2 小时前
极简 WSL2 教程:开发、部署大模型必备
前端·javascript
WebGISer_白茶乌龙桃2 小时前
基于 Cesium 的 GLB 建筑模型分层分房间点击拾取技术实现
前端·javascript·vue.js·webgl·cesium
黑眼圈子2 小时前
牛客刷题记录5
java·开发语言·学习·算法
NGC_66112 小时前
ConcurrentHashMap介绍
java·开发语言
JY.yuyu2 小时前
Java Web上架流程(Nginx反向代理+负载均衡 ,Apache配置,Maven安装打包,Tomcat配置)
java·开发语言·前端
紫_龙2 小时前
最新版vue3+TypeScript开发入门到实战教程之路由详解二
前端·javascript·typescript
Bert.Cai2 小时前
Python标识符详解
开发语言·python