跳转到内容

4.7 Next.js 快速上手:创建项目与目录结构

📌 基于 Next.js App Router · 2026-10 更新。Next.js 每年有较大版本更新,具体 API 以你项目里 package.json 的版本为准。本节教的是不会变的部分:目录约定、三个命令、以及那条最容易把新手卡住的 "use client" 规则。

一、本节你会做出什么

你会自己从零创建一个 Next.js 项目(这次搞懂每个选项在问什么),看懂 app/ 目录的约定,并让 pnpm build 第一次通过。

📷 效果位:终端里 build 成功的输出 + 浏览器里的页面

2.2 里你跑过一次 pnpm create next-app,当时是“一路回车”。这次你会知道自己在建什么。


二、开始前你需要

  • 已完成 4.6,知道组件、JSX、props、state
  • 已完成 4.5,知道 page.tsx 和 layout.tsx 的分工
  • node -v 和 pnpm -v 都能打印版本号(2.2)

三、跟着做

第 1 步:Next.js 在 React 之外加了什么

4.6 结尾那个类比:React 是发动机,Next.js 是整台车。 具体加了这五样:

Next.js 提供 没有它你要自己做 本课哪里用
路由 自己写一套“网址 → 显示哪个页面”的逻辑 4.8、5.6
服务端渲染 页面得等 JS 跑完才有内容,搜索引擎看不到 11.6(SEO)
图片优化 手动压缩、手动做不同尺寸 5.3
后端接口 单独起一个后端项目 7.7、8.3
构建与部署约定 自己配打包工具 11.2

🔑 第 4 条值得划重点:Next.js 项目里可以直接写后端代码。这意味着第 8、9 章你不需要再学一套新东西——还是这个项目,多加几个文件。这是本课选 Next.js 的主要原因。

第 2 步:创建项目,这次看懂每个选项

终端窗口
cd ~/Documents/ai-course # 换成你的路径
pnpm create next-app@latest nextjs-practice

💡 第 5 章会用同样的方式创建真正的项目「遇见成都」。这次先拿一个练习项目把流程走明白。

它会问你一串问题。这次别急着回车,看一眼每个在问什么:

它问什么 在问 本课选
Would you like to use TypeScript? 要不要类型检查(4.5 讲过的 .tsx) Yes(AI 写的代码出错更少)
Would you like to use ESLint? 要不要代码规范检查 Yes
Would you like to use Tailwind CSS? 要不要 4.3 学的那套 class Yes
Would you like your code inside a src/ directory? 代码外面要不要多包一层 src/ 都行(4.5 提过两种都常见)
Would you like to use App Router? 用新的路由方式还是旧的 Yes —— 这一项必须 Yes
Would you like to use Turbopack? 用更快的构建工具 Yes
Would you like to customize the import alias? 自定义导入路径别名 No

⚠️ 只有 App Router 那一项不能选错。 选 No 会得到旧的 pages/ 路由,本课后面所有内容(4.8 的路由、5.6 的详情页)都对不上。

选项问完它会下载依赖,1~3 分钟(网络慢的话参考 2.2 第五部分)。

📷 截图位 1:创建过程中的选项列表

第 3 步:跑起来,并认识热更新

终端窗口
cd nextjs-practice
pnpm dev

浏览器打开 http://localhost:3000。

现在做一件事,体会本地开发最舒服的一点:

  1. 用编辑器打开 app/page.tsx
  2. 随便改掉一段文字,保存
  3. 不要刷新浏览器 —— 看着它自己变了

这叫热更新(Hot Reload)。改完就见效,不用刷新、不用重启。

🔑 什么时候必须重启 pnpm dev(这三条记一下,能省很多困惑):

改了什么 要重启吗
页面、组件、样式 ❌ 不用,热更新自己处理
.env.local(环境变量) ✅ 必须重启(4.5 提过)
配置文件(next.config.ts 等) ✅ 必须重启
装了新依赖包 ✅ 必须重启

重启方法:终端里 Ctrl+C(2.1 的刹车),再 pnpm dev。

第 4 步:app/ 目录的文件名约定

这是 Next.js 最核心的设计:文件名本身就是配置。

你在 app/ 里放一个叫特定名字的文件,Next.js 就知道该拿它干什么——不需要你在任何地方注册。

文件名 是什么 必须有吗
page.tsx 这个网址对应的页面 ✅ 有它才有这个页面
layout.tsx 外壳,包着同目录及子目录的所有页面 根目录必须有
loading.tsx 加载中显示什么 可选
not-found.tsx 404 时显示什么 可选
error.tsx 出错时显示什么 可选
globals.css 全局样式 可选

layout.tsx 里有一个必须存在的东西:

export default function RootLayout({ children }) {
return (
<html lang="zh-CN">
<body>
{/* 这里放每页都有的东西,比如导航 */}
{children} {/* ← 页面内容会被塞进这个位置 */}
</body>
</html>
);
}

{children} 就是那个**“插页面的洞”**。没有它,页面内容无处可去,你会看到一个只有导航和页脚的空壳。

💡 4.5 那条判断现在有了技术解释: 「每页都有的东西放 layout.tsx」—— 因为 layout 包着所有页面,写一次,每页都有。

第 5 步:⚠️ "use client" —— 这一节最该记住的一条

这是 App Router 让零基础学员卡住最多的一个点。 花三分钟理解它,能省你几个小时。

Next.js 里的组件分两种:

服务端组件(默认) 客户端组件
在哪运行 服务器上,渲染好 HTML 发给浏览器 浏览器里
能用 useState 吗 ❌ 不能 ✅ 能
能用 onClick 吗 ❌ 不能 ✅ 能
能直接读数据库吗 ✅ 能(第 9 章) ❌ 不能
怎么声明 什么都不用写(默认就是) 文件第一行写 "use client"

所以会发生这件事:

你让 AI 加一个“点击展开”的功能,它写了 useState 和 onClick,然后页面报错:

Error: useState only works in Client Components.
Add the "use client" directive at the top of the file to use it.

解法就一行,加在文件最顶上(比所有 import 都靠前):

"use client";
import { useState } from "react";
...

🔑 记住这个因果: 要交互(点击、输入、状态变化)→ 文件顶部加 "use client"。

报错信息其实已经把答案写出来了(Add the "use client" directive)——这是第 6.1 节「报错信息怎么读」的一个好例子:很多报错自带解法。

什么时候不用加:纯展示的页面和组件(景点介绍、文章内容)保持默认的服务端组件更好——更快、对 SEO 更友好。

💡 实用做法:别纠结该不该加。先不加,报错了再加——报错信息会明确告诉你。或者在规则文件(3.5)里写一条:「需要交互的组件记得加 "use client"」。

第 6 步:三个命令,以及为什么要本地 build 一次

package.json 的 scripts 里(4.5 讲过)有这三个:

命令 干什么 什么时候用
pnpm dev 启动开发服务器,带热更新 日常开发,用得最多
pnpm build 打包成可部署的产物 部署前,以及检查错误
pnpm start 运行打包后的产物 本地模拟线上环境

现在执行一次 build(先 Ctrl+C 停掉 dev):

终端窗口
pnpm build

成功的话,你会看到一张路由表,列出生成了哪些页面。

📷 截图位 2:build 成功的输出,含路由列表

🔑 为什么这一步值得单独教:

pnpm dev 能跑,不代表 pnpm build 能过。

dev 模式宽容,build 严格(类型错误、未使用的变量、服务端/客户端组件用错,都可能在这里才暴露)。

4.1 定位表最后一行「本地好的,上线就坏」,一半的情况就是这个 —— 部署平台做的第一件事就是 build,它失败了,网站就上不去。

养成习惯:提交或部署之前,本地 pnpm build 跑一次。 这一条能帮你省掉很多“部署失败”的排查。

第 7 步:动手——给练习项目加点东西

让 AI 干活,用 3.5 的方式描述。先存档:

终端窗口
git init # 新项目还没初始化 Git
git add -A && git commit -m "初始化 Next.js 练习项目"

然后在项目目录里启动 AI 工具,说:

【做什么】把首页改成一个简单的个人介绍页
【放在哪】app/page.tsx;导航栏放到 app/layout.tsx 里
【什么样】
- layout 里加一个顶部导航,显示站点名"我的练习站"
- 首页包含:一个大标题、一段自我介绍、一个「点我」按钮
- 按钮点击后,下面显示一行"你点了 N 次"(N 会累加)
- 用 Tailwind,风格简洁,手机上也要正常
【不要做什么】
- 不要新增任何依赖包
- 不要改配置文件
【怎么算对】pnpm dev 下点击按钮数字会涨,且 pnpm build 能通过
先告诉我你打算改哪些文件,我确认后再动手。

注意观察一件事:按钮需要 useState,所以 AI 应该会给对应文件加上 "use client"。如果它忘了,你会看到第 5 步那条报错——这时你知道该怎么办了。

验收(3.5 的四种验收):

终端窗口
git diff --stat # ③ 改动范围
pnpm build # 本节新增的一道关:build 必须过

浏览器里点一下按钮(② 操作验收),看控制台有没有红字。都过了就存档:

终端窗口
git add -A && git commit -m "首页改成个人介绍页,加上计数按钮"

四、完成的标志是

  • 我说得出 Next.js 在 React 之外提供了什么(至少说出路由和“能写后端”两条)
  • 我自己创建了一个 Next.js 项目,并且知道 App Router 那一项必须选 Yes
  • 我体验了热更新:改完保存,不刷新也变了
  • 我知道改 .env.local 和配置文件必须重启 dev
  • 我知道 page.tsx 是页面、layout.tsx 是外壳
  • 我知道 layout.tsx 里必须有 {children},那是“插页面的洞”
  • 我理解 "use client":要交互就加在文件第一行
  • 我知道服务端组件里不能用 useState 和 onClick
  • 我知道 dev / build / start 三个命令分别干什么
  • 我本地跑过一次 pnpm build 并通过了
  • 我理解“dev 能跑不代表 build 能过”,且知道这是“部署失败”的常见原因
  • 我完成了第 7 步的改造,按钮能计数

五、卡住了看这里

Q:报错 useState only works in Client Components。 第 5 步那一条:在报错文件的最顶部(所有 import 之前)加 "use client";。这是本节最高频的一个问题。

Q:报错 Event handlers cannot be passed to Client Component props 或提到 onClick。 同一个原因——你在服务端组件里写了事件处理。加 "use client"。

Q:pnpm dev 报端口被占用。 2.2 第五部分讲过:找到旧终端 Ctrl+C,或用 pnpm dev -- -p 3001 换端口。

Q:pnpm dev 能跑,但 pnpm build 报一堆错。 这正是要本地 build 的意义。 把完整报错给 AI(用本节提示词 ②)。常见原因:类型错误、"use client" 用错位置、引用了不存在的文件。不要跳过 build 直接部署 —— 部署平台会在同一个地方失败,而那边的日志更难读。

Q:页面一片空白,控制台也没报错。 先看 layout.tsx 里有没有 {children}。少了它,页面内容就不会被渲染出来,而且不报错。

Q:我创建项目时选错了选项(比如 App Router 选了 No)。 练习项目最省事的办法:删掉重建。cd .. 然后 rm -rf nextjs-practice,重新 create 一次。练习阶段重建比修改便宜得多。

Q:那些选项我还是记不住,下次创建怎么办? 用本节提示词 ①,让 AI 给你一条带全部参数的命令,一行搞定,不用交互式选择。


六、本节提示词

① 让 AI 给你一条确定的创建命令(避免下次又在选项里犯错):

我要创建一个新的 Next.js 项目,用 pnpm。
要求:TypeScript、Tailwind CSS、ESLint、**App Router**、不自定义 import alias。
项目名:【你的项目名】
请给我一条**带上全部参数、不需要交互式回答问题**的完整命令,
并逐个参数说明它是什么意思。
如果某个参数名在当前版本可能不同,提醒我以 `pnpm create next-app --help` 为准。

② build 失败急救(本节最该存的一条):

我的项目 `pnpm dev` 能正常运行,但 `pnpm build` 失败了。
完整的构建输出如下(从命令开始到报错结束,一字不删):
【粘贴】
请你:
1. 指出是哪个文件、哪一行的问题;
2. 用大白话说明这是什么问题,以及**为什么 dev 能过而 build 不能**;
3. 给出修复方案,**先告诉我方案不要直接改**;
4. 修完之后我应该怎么验证(具体命令)。

③ 判断某个组件该不该加 "use client":

这是我的组件代码:
【@文件名 或粘贴代码】
请告诉我:
1. 它需要加 `"use client"` 吗?为什么?
2. 如果需要,加在哪一行(我要确切位置);
3. 如果不需要,说明它为什么可以保持服务端组件;
4. 有没有办法把需要交互的那一小部分单独拆成客户端组件,让页面其余部分保持服务端渲染?
如果有,说明拆分方案的好处。

上一节 ← 4.6 为什么需要前端框架 下一节 → 4.8 Next.js 核心:路由与页面跳转