大家好,我是程序员柏风,AI 最全的 Vibecoding 学习地图 http://coderoadmap.cn 作者。
Next.js 概览
这一课你将能做到
- 说清楚 Next.js 与纯 React 的区别,以及它解决了什么问题
- 用
create-next-app从零初始化一个可运行的 Next.js 项目 - 区分 App Router 与 Pages Router,并知道新项目应该选哪个
- 理解文件路由的基本约定:文件夹即路径、
page.tsx即页面
为什么要学
React 只是一个 UI 库,本身不管路由、不管服务端渲染、不管打包优化。一个真实的生产项目要自己拼装路由(react-router)、SSR 方案、代码分割、图片优化、SEO 处理……每一项都是坑。Next.js 把这些全打包成一个有意见的框架:你按它的约定写,它给你 SSR/SSG、文件路由、图片优化、字体优化、API 路由、流式渲染,开箱即用。
人话:React 是发动机,Next.js 是整车。你当然可以只买发动机自己攒车,但大多数人选择直接买整车开走。
面试层面,"React 和 Next.js 的区别"、"为什么用 Next.js" 是初中级前端面试的高频开场题,答不清楚直接出局。
先建立图像
一个最小 Next.js 项目的目录骨架:
my-app/
├── app/
│ ├── layout.tsx ← 全局布局(html/body 在这里)
│ ├── page.tsx ← 首页(访问 / 时渲染)
│ ├── globals.css ← 全局样式
│ └── about/
│ └── page.tsx ← /about 页面(文件夹名决定 URL)
├── public/
│ └── logo.svg ← 静态资源,通过 /logo.svg 访问
├── next.config.ts ← Next.js 配置
├── package.json
└── tsconfig.json
关键直觉:app/ 下的文件夹结构 = URL 路径结构,文件夹里的 page.tsx 是该路径的页面组件。不需要配路由表。
跟着做
步骤 1:环境准备
确认本机有 Node.js 18.18 或更高版本(Next.js 15 要求):
node -v
如果低于 18.18,去 nodejs.org 下载 LTS 版本。
步骤 2:创建项目
打开终端,进入一个空目录,执行:
npx create-next-app@latest my-first-next
会出现一组交互提问,按下面的选择(这是当前推荐组合):
✔ Would you like to use TypeScript? … Yes
✔ Would you like to use ESLint? … Yes
✔ Would you like to use Tailwind CSS? … Yes
✔ Would you like your code inside a `src/` directory? … No
✔ Would you like to use App Router? (recommended) … Yes
✔ Would you like to use Turbopack for `next dev`? … Yes
✔ Would you like to customize the import alias (`@/*` by default)? … No
完成后进入项目并启动开发服务器:
cd my-first-next
npm run dev
浏览器打开 http://localhost:3000,能看到 Next.js 默认欢迎页,说明项目跑通了。
步骤 3:改一下首页
打开 app/page.tsx,把默认内容全部删掉,替换成:
export default function Home() {
return (
<main style={{ padding: 40 }}>
<h1>我的第一个 Next.js 页面</h1>
<p>当前时间:{new Date().toLocaleString('zh-CN')}</p>
</main>
);
}
保存后浏览器自动热更新,能看到新内容。注意:这个组件默认是 Server Component,时间在服务端渲染时生成。
步骤 4:创建第一个子路由
新建文件夹 app/about,里面新建 page.tsx:
export default function AboutPage() {
return (
<main style={{ padding: 40 }}>
<h1>关于我们</h1>
<p>这个页面对应的 URL 是 /about</p>
<a href="/">返回首页</a>
</main>
);
}
访问 http://localhost:3000/about,能看到这个页面。没有写任何路由配置——这就是文件路由。
步骤 5:看一眼 layout.tsx
打开 app/layout.tsx,这是整个应用的根布局:
import type { Metadata } from 'next';
import './globals.css';
export const metadata: Metadata = {
title: '我的 Next.js 应用',
description: '学习 Next.js 的第一个项目',
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="zh-CN">
<body>{children}</body>
</html>
);
}
children 就是当前路径对应的 page.tsx 渲染结果。所有页面都被这个 layout 包裹。
步骤 6:体验静态资源
把任意一张图片命名为 avatar.png 放到 public/ 目录,然后在 app/page.tsx 里引用:
export default function Home() {
return (
<main style={{ padding: 40 }}>
<h1>我的第一个 Next.js 页面</h1>
<img src="/avatar.png" alt="头像" width={100} />
</main>
);
}
public/ 下的文件直接以根路径对外提供,无需任何配置。
讲清楚要点
- Next.js 是 React 的生产框架:在 React 之上提供路由、渲染策略、构建优化、API 层。Vercel 官方维护,React 团队官方推荐的生产方案。
- App Router 是当前标准:Next.js 13 引入、13.4 起稳定、15 完全成熟。基于 React Server Components,把"默认在服务端渲染"作为一等公民。Pages Router 是旧方案(
pages/目录),仅在为旧项目维护时接触。 - 文件路由 = 约定大于配置:
app/about/page.tsx→/about,app/blog/[id]/page.tsx→/blog/任意值。新增页面就是新建文件夹+page.tsx,删除同理。 - Server Component 优先:
app/下所有组件默认是服务端组件,只在服务器执行、不打包进客户端 JS。需要交互(onClick、useState)时,文件顶部加'use client'显式降级为客户端组件。 layout.tsx持久化:路由切换时 layout 不会重新渲染,状态保留,适合做导航栏、侧边栏。- 内置优化:
<Image>自动压缩裁剪、<Link>自动预取、next/font自动托管字体——这些不是可选项,是默认就该用的。
你可能会懵的地方
-
Next.js 和 React 到底什么关系? React 是 UI 库(只负责把数据变成 DOM),Next.js 是基于 React 的框架(管路由、渲染、构建、部署)。你写的组件还是 React 语法,但项目是 Next.js 项目。
-
App Router 和 Pages Router 选哪个? 新项目一律 App Router。Pages Router 是 Next.js 12 及之前的方案,官方仍在维护但不再新增功能。判断标准:项目里是
app/目录就是 App Router,是pages/目录就是 Pages Router。 -
为什么我直接写
onClick报错?app/下组件默认是 Server Component,不能有任何浏览器交互。要么把事件处理挪到子组件并加'use client',要么整个文件加'use client'。 -
page.tsx必须叫这个名字吗? 必须。Next.js 只认这几个保留文件名:page.tsx、layout.tsx、loading.tsx、error.tsx、not-found.tsx、template.tsx、route.ts。换成index.tsx不会生效(这是和 Pages Router 的重要区别)。 -
<a href>和<Link>用哪个? 站内跳转一律用next/link的<Link>,它会做客户端跳转 + 自动预取,体验接近 SPA。站外链接、下载链接、需要整页刷新的场景才用<a>。 -
public/和app/都能放静态文件吗? 静态资源(图片、字体、favicon、robots.txt)统一放public/。app/下也能放icon.png、opengraph-image.png这种约定文件名的资源,但那是元数据用法,下一课会讲。
错法对照
| 错法 | 问题 | 正解 |
|---|---|---|
在 app/ 下建 index.tsx 当页面 | App Router 只认 page.tsx | 重命名为 page.tsx |
Server Component 里写 useState | 服务端组件不能有状态 | 文件顶部加 'use client' |
站内跳用 <a href="/about"> | 整页刷新,丢失 SPA 体验 | 用 import Link from 'next/link' |
把图片放 app/images/ 下用 <img src> | 不会作为静态资源对外服务 | 放 public/images/,用 next/image |
在 app/page.tsx 里同时导出 layout | 职责混乱,构建报错 | layout 写到 app/layout.tsx |
用 Pages Router 思路找 _app.tsx | App Router 没有 _app | 全局配置写到 app/layout.tsx |
卡点急救
npx create-next-app卡住不动:网络问题,切换 npm 镜像:npm config set registry https://registry.npmmirror.com后重试。npm run dev报Port 3000 is in use:3000 端口被占用,改用npm run dev -- -p 3001,或找到占用进程杀掉。- 页面报
hydration failed:服务端渲染的 HTML 和客户端首渲染结果不一致,常见原因是new Date()、Math.random()、window直接出现在组件顶层。把这类代码移到useEffect或'use client'组件。 - 热更新不生效:先 Ctrl+C 停掉 dev server 再重启;仍不行删
.next/缓存目录后重启。 - TypeScript 报
Cannot find module './xxx':路径写错或文件没保存。Next.js 默认 alias@/*指向项目根,可以用@/app/...替代相对路径。
面试常问
-
Next.js 解决了什么问题?为什么不用纯 React? 纯 React 是 SPA 方案,首屏白屏时间长、SEO 不友好、路由/数据获取/构建都要自己拼。Next.js 提供:① SSR/SSG/ISR 多种渲染策略,首屏快且 SEO 友好;② 文件路由开箱即用;③ Server Components 减少客户端 bundle;④ 图片/字体/脚本的内置优化;⑤ API 路由让前后端同仓。
-
App Router 和 Pages Router 的核心区别? ① 渲染模型:App Router 默认 Server Component,Pages Router 默认客户端组件 + getServerSideProps/getStaticProps;② 数据获取:App Router 直接在组件里
await fetch,Pages Router 用专门的getServerSideProps函数;③ 布局:App Router 支持嵌套 layout 且切换不重置状态;④ 文件约定:App Router 用page.tsx+layout.tsx,Pages Router 用pages/xxx.tsx+_app.tsx。 -
什么是 Server Component? 只在服务端执行的 React 组件,渲染结果直接以 HTML/RSC payload 返回,不打包到客户端 JS。优点:可以直读数据库、零 bundle 体积、自动代码分割。代价:不能用 hooks、不能事件绑定。
-
什么场景下不应该用 Next.js? 纯后台管理系统(SEO 不重要、登录后全是客户端交互)用 Vite + React 反而更轻量;浏览器扩展、Electron 主进程这种没有服务器的场景也用不上 Next.js 的服务端能力。
自测三问
app/blog/[id]/page.tsx对应的 URL 是什么?[id]是什么含义?- 一个文件加上
'use client'后,它还能直接await数据库查询吗?为什么? - 要把一张图片以
/logo.png路径对外提供,应该放在项目哪个目录下?
小结一句话
Next.js = React + 路由 + 渲染策略 + 工程化优化,App Router 用「文件夹即路由、默认 Server Component」两条约定,把生产级前端项目的复杂度压到最低。
延伸
- Next.js 官方文档 - Getting Started
- React Server Components RFC
- 下一课:App Router 深入——动态路由、嵌套路由、路由组、拦截路由、并行路由的完整玩法。