CodeRoad Map 程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。
由 程序员柏风 独立维护 · 关于作者
学习 更多
项目创建与目录
这一课你将能做到
不看文档就能说清 app/、public/、next.config.ts、middleware.ts 各自放什么
创建一个带 src/ 目录的项目,并亲手添加页面、组件、工具函数
配置环境变量,区分 NEXT_PUBLIC_ 前缀和服务端专用变量的安全差异
为什么要学
目录结构就是框架的「语法」。Next.js 的路由、渲染、报错定位,全都建立在文件位置之上:文件放错地方,页面就是 404;配置写错文件,功能就是不开。初学者最常见的困境不是「不会写代码」,而是「不知道这行代码该放哪个文件夹」——本课就是解决这个问题的。
本站 platform 项目你以后要读,它就是标准结构:src/app 放页面与路由、src/components 放组件、src/lib 放工具与 API 封装。现在把「标准结构」刻进肌肉记忆,第 11 课读源码时你会像看自己家一样熟悉。
面试角度,这是一个隐形高频题:「介绍一下你们项目的目录结构 / Next.js App Router 的约定式路由怎么组织」。答得有条理,直接体现你真做过项目。
先建立图像
create-next-app 生成的完整目录,加注释版:
my-first-next/
├── src/ # 源码根(--src-dir 时才有)
│ ├── app/ # ★ 路由核心:文件夹即 URL
│ │ ├── layout.tsx # 根布局:所有页面共享的外壳
│ │ ├── page.tsx # 首页 /
│ │ ├── globals.css # 全局样式
│ │ └── favicon.ico # 站点图标
│ ├── components/ # (自己建)可复用组件
│ └── lib/ # (自己建)工具函数、API 封装
├── public/ # 静态资源:/logo.png 直接 URL 访问
├── next.config.ts # Next.js 配置:图片域名、重定向、MDX…
├── tsconfig.json # TypeScript 配置(@/ 别名在这里定义)
├── postcss.config.mjs # Tailwind/PostCSS 配置
├── package.json # 依赖与脚本
└── .env.local # 本地环境变量(不提交 git)
三条记忆钩子:
URL 相关 → src/app/ :页面、布局、接口都在这,靠文件夹名字生成路由
被 URL 直接引用 → public/ :public/cat.png 的访问地址就是 /cat.png
其余全是普通前端项目 :components、lib、hooks 想怎么拆怎么拆
跟着做
步骤 1:创建项目
npx create-next-app@latest my-app --typescript --tailwind --eslint --app --src-dir
cd my-app
npm run dev
打开 http://localhost:3000 确认默认首页正常。
步骤 2:添加第一个新页面
在 src/app/about/ 下新建 page.tsx:
export default function AboutPage() {
return (
<main className="p-8">
<h1 className="text-2xl font-bold">关于本站</h1>
<p className="mt-2 text-gray-600">这是一个 Next.js 学习项目。</p>
</main>
);
}
保存后直接访问 http://localhost:3000/about。注意:没有注册路由、没有改任何配置 ——文件夹名就是 URL。这就是「文件路由」。
步骤 3:建立 components 和 lib 目录 # macOS/Linux
mkdir -p src/components src/lib
# Windows PowerShell
mkdir src\components, src\lib
先建工具函数 src/lib/greeting.ts:
export function greeting(name: string): string {
const hour = new Date().getHours();
if (hour < 6) return `夜深了,${name}`;
if (hour < 12) return `早上好,${name}`;
if (hour < 18) return `下午好,${name}`;
return `晚上好,${name}`;
}
再建组件 src/components/WelcomeCard.tsx:
import { greeting } from "@/lib/greeting";
export default function WelcomeCard({ name }: { name: string }) {
return (
<div className="rounded-lg border p-4 shadow-sm">
<h2 className="text-lg font-semibold">{greeting(name)}</h2>
<p className="mt-1 text-sm text-gray-500">欢迎使用本站。</p>
</div>
);
}
在 src/app/about/page.tsx 里使用:
import WelcomeCard from "@/components/WelcomeCard";
export default function AboutPage() {
return (
<main className="p-8">
<h1 className="text-2xl font-bold">关于本站</h1>
<WelcomeCard name="同学" />
</main>
);
}
注意 @/ 前缀:这是 tsconfig.json 里配置的路径别名,指向 src/。本站代码里满屏的 @/components/... 就是它。
步骤 4:认识根布局 layout.tsx import type { Metadata } from "next";
import "./globals.css";
export const metadata: Metadata = {
title: "我的学习站",
description: "Next.js 学习项目",
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="zh-CN">
<body>{children}</body>
</html>
);
}
两个关键点:每个页面都会被 <body>{children}</body> 包裹(导航栏、页脚就该放这);metadata 导出会变成 <title> 和 <meta> 标签——SEO 的第一入口。
步骤 5:环境变量 在项目根目录新建 .env.local(该文件默认被 git 忽略):
# 浏览器可见:必须以 NEXT_PUBLIC_ 开头
NEXT_PUBLIC_API_URL=http://localhost:8080
# 仅服务端可见:不加前缀,永远不会打进客户端代码
API_INTERNAL_URL=http://backend-internal:8080
DATABASE_URL=postgresql://user:pass@localhost:5432/db
// 任何地方都能读公开变量
const apiUrl = process.env.NEXT_PUBLIC_API_URL;
// 只有 Server Component / route.ts / lib 的服务端代码能读私密变量
const dbUrl = process.env.DATABASE_URL;
本站 platform 的 src/lib/apiBase.ts 就是这么写的:公开地址用 NEXT_PUBLIC_API_URL,服务端 SSR 时优先走内网直连的 API_INTERNAL_URL,避免绕公网拉数据。改完环境变量必须重启 npm run dev 才生效。
步骤 6:next.config.ts 长什么样 import type { NextConfig } from "next";
const nextConfig: NextConfig = {
images: {
remotePatterns: [{ protocol: "https", hostname: "images.example.com" }],
},
async redirects() {
return [{ source: "/old-about", destination: "/about", permanent: true }];
},
};
export default nextConfig;
现在只需知道:图片域名白名单、重定向、MDX 支持(第 8 课会用到这里)都改这个文件。
讲清楚要点
src/app/ 是唯一有魔法的目录 :里面的文件名是约定(page.tsx、layout.tsx、后续会见的 route.ts),放错名字就不生效。components/、lib/ 完全自由,叫什么都行。
page.tsx 才生成路由 :只建文件夹不建 page.tsx,URL 是 404。反过来,src/app/about/ 里写个 helper.ts 不会变成路由——非约定文件名一律当普通模块。
public 没有构建过程 :放进去叫什么,URL 就是什么,适合 favicon、字体、下载文件。需要压缩优化的图片用 next/image(可放 public 或远程)。
环境变量是「发布时」注入的 :不是运行时读的。NEXT_PUBLIC_ 变量在构建时被文本替换进 JS 包——所以绝不能把密钥放 NEXT_PUBLIC_ 变量里 ,等同公开。
@/ 别名默认指向 src/ :深层目录导入不用再 ../../..。看到本站代码 import { getTrack } from "@/lib/api" 就知道是 platform/src/lib/api.ts。
你可能会懵的地方
components/、lib/、hooks/ 是 Next.js 规定要建的吗?
不是。Next.js 只规定 app/(和 public/)。components、lib、hooks、services 都是社区习惯,你可以按团队喜好组织。本站就是 src/components + src/lib 这套。
page.tsx、layout.tsx 后缀能是 .js 或 .jsx 吗?
可以。.js、.jsx、.tsx、.ts 都认。课程统一用 .tsx(TS + JSX),建议你保持一致。
.env.local 和 .env 有什么区别?
.env 所有环境加载、可提交 git(放无敏感的默认值);.env.local 本地覆盖、约定不提交(放密钥)。优先级 local > 普通。还有 .env.production 只在生产构建加载。
修改环境变量后为什么页面行为没变?
环境变量在进程启动时读取。必须 Ctrl+C 停掉 dev server 再 npm run dev。这是新手坑频率前三的问题。
我应该在 app/ 目录里写业务逻辑吗?
页面文件(page/layout/route)保持「组装层」:取数据、调组件、返回 JSX。业务逻辑沉到 lib/,页面才清爽。本站的页面普遍很薄,逻辑都在 src/lib/。
错法对照 错法 问题 正解 src/app/about.tsx 想生成 /about文件路由认文件夹不认散文件 建 src/app/about/page.tsx 在 app/ 里建 Page.tsx(大写 P) 约定文件名小写,不识别 严格用 page.tsx 密钥写进 NEXT_PUBLIC_SECRET 会打进客户端 JS,全网可见 无前缀变量,仅服务端读取 把图片放进 src/assets 后用 /assets/x.png 引用 src 下文件没有 URL 放 public/,或用 import img from "@/assets/x.png" 改了 .env.local 不重启就测 变量还是旧值 重启 dev server
卡点急救
页面 404 :按顺序查——文件夹名拼写?有没有 page.tsx?dev server 是否在跑?URL 是否多了少了斜杠。
Module not found: Can't resolve '@/lib/xxx' :路径别名没生效,检查 tsconfig.json 里 "paths": { "@/*": ["./src/*"] };或文件确实不存在/扩展名写错。
浏览器控制台报 process.env.XXX is undefined :如果是 NEXT_PUBLIC_ 变量→重启 dev;如果是无前缀变量→你把它用在了 Client 组件里,只能改为服务端读取或换公开变量。
面试常问
介绍一下 App Router 项目的目录约定?
核心是 app/ 目录的文件约定:page.tsx 定义路由 UI、layout.tsx 定义嵌套布局、loading.tsx/error.tsx/not-found.tsx 处理加载/错误/404、route.ts 定义 API 接口;[param] 文件夹声明动态段。公共组件、工具函数放在 app 外(如 src/components、src/lib),静态资源放 public/。
NEXT_PUBLIC_ 前缀的环境变量有什么风险?
它会在构建时被内联进客户端代码,任何用户查看页面源码/JS 包都能拿到。因此只放公开信息(如 API 地址),所有密钥(数据库连接串、JWT secret、第三方 API key)必须用无前缀变量并只在服务端读取。本站把后端地址设为公开变量、把内部直连地址设为服务端变量,就是这个原则的应用。
为什么推荐 src/ 目录?
把「框架约定的 app/」和「自己的代码」都收进 src,根目录只留配置文件,层次更清晰;同时也避免某些工具误把根目录下的杂散 JS 当源码。纯属工程习惯,不加 src 也完全合法。
自测三问
src/app/blog/page.tsx 和 src/app/blog/index.tsx,哪个会生成 /blog 路由?
一个第三方 API key,应该放在哪种环境变量里?在哪里读它?
public/logo.svg 对应的访问 URL 是什么?
小结一句话 目录结构即框架语法:app/ 管路由、public/ 管静态资源、lib/components 管你自己的代码,环境变量用前缀区分「公开」与「私密」。
延伸