Next.js 16.2+ + React 19.2+ + TypeScript

Next.js 架构与 AI 编程指南

写给刚上手 Next.js 的你:项目文件怎么分、组件怎么拆、跟 AI 怎么配合,这里都有真实例子照着做。

浅色系 · Mac 风格 · 附问卷项目重构实例

目录

  1. 为什么需要学 Next.js 架构?
  2. Next.js 16+ App Router 目录规范
  3. 如何指挥 AI 写代码?
  4. 问卷项目重构实例
  5. Next.js 16 新功能一览
  6. 安全基线:为什么不要用旧版本?
  7. AI 编程 PADC 流程
  8. 简单测试规范
  9. 附录:项目目录速查
第一章 · 为什么需要学 Next.js 架构?

代码多了,不迷路才重要

刚接触 Next.js 项目时,大部分人想法都一样:“能跑起来就行”。 可项目一旦从十几个文件长到几十个、上百个,麻烦通常逃不开这三种:

❌ 找不到代码

数据、组件全堆在 page.tsx 里,想改一个按钮的圆角,得先翻完 300 行代码才找得到在哪儿。

❌ AI 越改越乱

你说一句“帮我优化一下”,AI 这改一处那改一处,改完你发现——多了个新 bug。

❌ 不好复用

每个页面都自己写一遍按钮样式,翻到第五个页面你就会发现,项目里躺着七八种长得不一样的按钮。

架构规范,说到底是为“人”定的

写规范图的不是好看,是让不同的人——包括未来的你,还有帮你写代码的 AI——按同一套规矩干活。 好架构大概长这样:

  • 路由统一:文件夹就是 URL,不需要手写路由表。
  • 文件行数不多:一个文件只干一件事,超过 200 行就该拆了。
  • 板块分得清:数据放数据该在的地方,UI 只管展示,状态和计算逻辑也不掺一起。
  • 能复用:通用的东西抽出来,哪个页面要用直接拿。
小白的判断法: 拿不准一段代码该放哪,就问自己一句——“这是数据、是长相(UI),还是某种状态?” 归好类之后,下次找东西直接进对应文件夹,不用翻遍整个项目。
第二章 · Next.js 16+ App Router 目录规范

App Router 的最大变化:文件即路由

Next.js 16 继续使用 App Router。在这个体系里,你不用写路由配置文件, 只要按照规则在 app/ 目录下放文件,Next.js 会自动帮你生成路由。 本章以 Next.js 16.2+ 为基线讲解,兼顾 15.x 用户的升级路径。

1. 路由约定

app/ ├── page.tsx → / ├── layout.tsx → 根布局,包裹所有页面 ├── about/ │ └── page.tsx → /about └── dashboard/ └── page.tsx → /dashboard
注意:在 App Router 里,只有 page.tsx 才会暴露为路由。 其他文件(比如 components.tsx、utils.ts)不会变成 URL。

2. 把源码放进 src/ 目录

默认 app/ 可以直接放在项目根目录。但推荐把它放进 src/app/, 这样项目根目录只留下配置文件和文档,业务代码都集中在一起。

my-questionnaire/ ├── src/ │ ├── app/ │ │ ├── layout.tsx │ │ ├── page.tsx │ │ └── globals.css │ ├── components/ │ │ ├── ui/ │ │ │ └── Button.tsx │ │ └── questionnaire/ │ │ ├── OptionItem.tsx │ │ ├── QuestionCard.tsx │ │ ├── QuestionSection.tsx │ │ ├── ResultView.tsx │ │ └── Questionnaire.tsx │ └── lib/ │ ├── data/ │ │ └── questionnaire.ts │ ├── types/ │ │ └── questionnaire.ts │ └── utils/ │ └── calculateScore.ts ├── docs/ ├── scripts/ ├── public/ ├── README.md ├── AGENTS.md ├── next.config.js ├── tailwind.config.js ├── tsconfig.json └── package.json

3. 服务器组件 vs 客户端组件

Next.js App Router 里,所有组件默认都是服务器组件—— 它们不会跑到浏览器里,是直接在服务器上渲成 HTML 发给用户。带来的结果是:

  • 服务器组件可以访问数据库、文件系统等后端资源。
  • 服务器组件不会被打包到浏览器,首屏加载更小。
  • 服务器组件里不能写 useState、onClick、window。

什么时候需要加 'use client' 变成客户端组件?

需要 'use client'

  • 用了 useState 或 useEffect
  • 有 onClick / onChange 事件
  • 调用了 document / window

不需要 'use client'

  • 只是展示数据
  • 导入数据并传给子组件
  • 不涉及用户交互
小白原则:不确定的时候,先不写 'use client'。如果 TypeScript 或 Next.js 报错说不能用 hooks,再加。

4. 路由组与私有文件夹

Next.js 还提供两个很有用的命名约定:

  • 路由组 (route groups):用括号 (folderName) 命名,只用于组织代码,不影响 URL。例如 app/(marketing)/page.tsx 仍然是 /。
  • 私有文件夹 (private folders):用下划线 _folderName 命名,Next.js 会忽略它,里面的文件不会变成路由。常用来放通用组件或工具。
第三章 · 如何指挥 AI 写代码?

没有规矩,AI 只会把项目改得更乱

AI 写代码确实快,但快不代表靠谱。没有一套清楚的规则,它就会“自由发挥”—— 文件乱放、样式重复写、服务器组件和客户端组件也分不清楚。

1. 用 AGENTS.md 给 AI 定规矩

AGENTS.md 是专门给 AI 读的项目规则。它告诉 AI:

  • 技术栈是什么(Next.js 16.2+、React 19.2+、TypeScript、Tailwind)。
  • 目录约定是什么(业务代码放 src/、组件怎么分层、数据放哪里)。
  • 编码规范是什么(不用 any、文件顶部写注释、超过 200 行拆分)。
  • 禁止事项(不要改 node_modules、不要忽略 TS 错误)。
AGENTS.md 的关键:它会被反复读取和更新。每次 Check 阶段发现新的问题,就把规则补充进去,下一轮 AI 就不会再犯。

2. 需求 → 规划 → 实现 → 验证

不要直接对 AI 说“帮我优化页面”。建议按下面的顺序:

  1. 先让 AI 描述当前项目结构。
  2. 告诉它你的目标,让它列出具体步骤。
  3. 确认步骤后,逐个执行。
  4. 每完成一步,运行 npm run build 验证。

3. Next.js 核心架构思想

路由统一

文件夹就是 URL。AI 不需要配置路由,只需要把文件放到正确的目录。

文件行数不多

一个文件只做一件事。超过 200 行就该拆分,方便 AI 和人类阅读。

板块职责

数据、UI、状态、计算别揽在一个组件里写。拆开之后,AI 想改错都难。

多复用

通用 UI 组件和业务组件分层。同一个 Button 到处都能用。

4. 给 AI 的提示词示例

请按照以下规则重构项目:

1. 把 app/ 移到 src/app/,并更新 tsconfig.json 和 tailwind.config.js。
2. 把问卷数据从 page.tsx 抽到 src/lib/data/questionnaire.ts。
3. 把评分逻辑抽到 src/lib/utils/calculateScore.ts。
4. 在 src/components/questionnaire/ 下拆分组件:
   - OptionItem(单个选项)
   - QuestionCard(一道题)
   - QuestionSection(一个模块)
   - ResultView(结果页)
   - Questionnaire(总控状态)
5. 每个文件顶部写教学注释,说明文件归属和拆分原因。
6. 改完后运行 npm run build 验证。
7. 不要写 any,使用 @/ 路径别名。
提示词越具体,AI 输出越稳定。“重构”是模糊指令,但上面这份清单已经足够 AI 按步骤执行。
第四章 · 问卷项目重构实例

从 300 行 monolith 到清晰分层

原来的 app/page.tsx 把所有东西写在一个文件里: 问卷数据、状态管理、评分算法、选项渲染、结果页。超过 300 行。 现在我们把它拆成多层,每层只负责一件事。

1. 类型层:src/lib/types/questionnaire.ts

类型是项目的“契约”。先把数据结构说清楚,后面的数据文件和组件都按这个契约来。

export type QuestionType = 'radio' | 'checkbox'
export type Answer = string | string[]
export type AnswerMap = Record

export type Question = {
    id: string
    type: QuestionType
    label: string
    options: string[]
    weight?: number
}

export type Section = {
    id: string
    title: string
    gender?: 'male' | 'female'
    questions: Question[]
}

2. 数据层:src/lib/data/questionnaire.ts

问卷内容单独放到数据文件。以后增删题目、调整选项,只要改这个文件, 不用动组件。

import { Section } from '@/lib/types/questionnaire'

export const sections: Section[] = [
    {
        id: 'base',
        title: '📋 基础信息 & 家庭关怀',
        questions: [
            { id: 'q1', type: 'radio', label: '1. 您的年龄段是?', options: ['18-30岁', '31-45岁', '46-60岁', '60岁以上'] },
            // ...
        ]
    },
    // ...
]

3. 计算层:src/lib/utils/calculateScore.ts

评分是一个纯函数:输入问卷和答案,输出分数。它不参与 UI,可以单独测试。

export function calculateScore(sections: Section[], answers: AnswerMap): number {
    let totalScore = 0
    let totalWeight = 0

    sections.forEach(section => {
        section.questions.forEach(q => {
            if (!q.weight) return
            const answer = answers[q.id]
            if (!answer) return

            let scoreValue = 0
            if (Array.isArray(answer)) {
                const sum = answer.reduce((acc, val) => {
                    const idx = q.options.indexOf(val)
                    return acc + (idx >= 0 ? (idx / (q.options.length - 1)) * 10 : 0)
                }, 0)
                scoreValue = answer.length > 0 ? sum / answer.length : 0
            } else {
                const idx = q.options.indexOf(answer)
                if (idx >= 0) scoreValue = (idx / (q.options.length - 1)) * 10
            }

            totalScore += scoreValue * q.weight
            totalWeight += q.weight
        })
    })

    return totalWeight > 0 ? Math.round((totalScore / totalWeight) * 10) / 10 : 0
}

4. 组件层:层层递进

OptionItem

单个选项卡片,只负责一个 radio/checkbox 的样式和点击。

QuestionCard

一道题,包含题干和所有选项,处理单选/多选切换逻辑。

QuestionSection

一个模块,渲染模块标题和题目列表。

Questionnaire

总控组件,持有状态、处理提交/重置、组合所有模块。

5. 页面入口:src/app/page.tsx

页面入口现在非常薄:导入数据,传给客户端组件。它自己没有任何交互逻辑。

import { sections } from '@/lib/data/questionnaire'
import { Questionnaire } from '@/components/questionnaire/Questionnaire'

export default function Home() {
    return 
}
重构后的效果:
  • 每个文件都能一屏看完,不用来回翻。
  • 数据在数据层,类型在类型层,计算逻辑在计算层,UI 只管展示。
  • AI 改一个需求,不会顺手碰到十几个不相关的文件。
  • 想换问卷内容,改 src/lib/data/questionnaire.ts 一处就够了。
第五章 · Next.js 16 新功能一览

从 15 到 16,架构没有推翻,但默认值变了

Next.js 16 不是推倒重来的“新框架”——组件写法、App Router 规则、文件约定基本都没动。 真正变的是几个默认值:构建器换成 Turbopack、请求期 API 全面异步、缓存策略升级、几个老 API 被砍掉。

1. Turbopack 成为默认构建器

从 Next.js 16 开始,next dev 和 next build 默认使用 Turbopack, 不再需要 --turbopack 或 --turbo。

  • 新项目无需任何配置即可享受更快的编译速度。
  • 如果项目有自定义 webpack 配置,next build 会报错;需要迁移到 Turbopack 选项,或显式使用 --webpack。
  • 16.2 的默认应用在 next dev 启动速度上比 16.1 快约 87%。

2. 请求期 API 全面异步

Next.js 15 是“同步兼容过渡期”,16 彻底移除同步访问。 以下 API 只能异步读取:

  • params(在 page、layout、route、icon、opengraph-image、sitemap 中)
  • searchParams(在 page 中)
  • cookies()、headers()、draftMode()
// ❌ Next.js 16 不允许
export default function Page({ params }: { params: { id: string } }) {
  return <div>{params.id}</div>
}

// ✅ Next.js 16
export default async function Page({
  params,
}: {
  params: Promise<{ id: string }>
}) {
  const { id } = await params
  return <div>{id}</div>
}

3. middleware 重命名为 proxy

为了更清晰地表达“网络边界与路由转发”的职责,middleware.ts 被重命名为 proxy.ts。

  • 旧文件名和 export function middleware() 仍可用,但已被标记为 deprecated。
  • 新的 proxy 只支持 Node.js runtime,不再支持 Edge runtime。
  • 配置项也同步改名,例如 skipMiddlewareUrlNormalize → skipProxyUrlNormalize。

4. 缓存 API 升级

  • revalidateTag(tag) 单参数写法废弃,必须写成 revalidateTag(tag, 'max')。
  • 新增 updateTag():专用于 Server Action,实现 read-your-writes,修改后立即刷新缓存。
  • 新增 refresh():在 Server Action 中刷新客户端 router。
  • unstable_cacheLife / unstable_cacheTag 已稳定为 cacheLife / cacheTag。

5. Partial Prerendering 改为 cacheComponents

不再通过 experimental.ppr: true 或 segment 级 experimental_ppr 启用。 改为在 next.config.js 中配置:

const nextConfig = {
  cacheComponents: true,
}

module.exports = nextConfig

6. 开发体验与性能提升

  • Server Function Logging:开发时终端自动打印 Server Function 名称、参数、执行时间和所在文件。
  • Hydration Diff Indicator:水合不匹配时,错误覆盖层用 + Client / - Server 标出差异来源。
  • next start --inspect:生产服务器也能挂 Node.js 调试器。
  • RSC payload 反序列化:最高提升 350%,真实应用服务端渲染提速 25%–60%。
  • ImageResponse:2x–20x 提速,默认字体改为 Geist Sans。
  • <Link> transitionTypes:支持按导航方向/上下文触发不同 View Transition 动画。

7. 已移除的 API

  • AMP 支持(next/amp、AMP 配置)
  • next lint 命令(改用 ESLint / Biome 直接运行)
  • Runtime Configuration
  • experimental.dynamicIO、experimental.useCache
一句话总结: Next.js 16 干了三件事——把容易写错的异步 API 强制改对,把还在实验阶段的功能定下来能用了, 再把默认打包工具换成更快的 Turbopack。对问卷这种小项目,你只用记一句:读 params 或 searchParams 时别忘了加 await。
第六章 · 安全基线:为什么不要用旧版本?

版本没跟上,等于把已知漏洞留在生产环境里

Next.js 16.2.6 和 React 19.2.6 是目前的安全基线版本。 版本低于这个线,项目很可能还带着已公开的 CVE 在跑,生产环境的风险不是说说而已。

1. 2025-12 安全更新

官方安全公告修复了两个高危/中危漏洞:

  • CVE-2025-55184(高危,DoS): 构造特殊 HTTP 请求发送到 App Router endpoint,可在反序列化时触发无限循环,挂起服务进程。
  • CVE-2025-55183(中危,源码泄露): 特殊请求可让 Server Function 返回其他 Server Function 的编译后源码, 可能暴露业务逻辑或硬编码 secret。

初始补丁不完整,后续又通过 CVE-2025-67779 完成了完整修复。 这意味着“升级到某个中间版本”可能还不够,必须升到最新补丁版本。

2. 2026-05 安全更新

这次是一次大规模协调披露,共修复 13 个安全 advisory,覆盖:

  • 中间件/代理绕过:App Router segment-prefetch 鉴权绕过、动态路由参数注入绕过等。
  • 拒绝服务(DoS):React Server Components 协议层 DoS、Cache Components 连接耗尽等。
  • 服务端请求伪造(SSRF):处理 WebSocket upgrade 请求时的问题。
  • 缓存中毒:RSC 响应缓存投毒、RSC cache-busting 碰撞等。
  • 跨站脚本(XSS):App Router CSP nonce、beforeInteractive 脚本等场景。

3. 受影响版本与推荐版本

Next.js 13.x / 14.x → 全部受影响 → 升级到最新 14.2.x Next.js 15.x → ≤ 15.5.17 → 升级到 15.5.18 Next.js 16.x → ≤ 16.2.5 → 升级到 16.2.6 react-server-dom-* → ≤ 19.2.5 → 升级到 19.2.6
为什么不要停留在旧版本? 这些漏洞多数发生在框架底层(RSC 协议解析、路由匹配、缓存层), 普通业务代码无法通过“自己写校验”来完全规避。唯一的完整缓解措施就是升级。

4. 如何检查项目版本

每次开发前或升级后,先执行以下命令:

# 1. 查看 Next.js 实际安装版本
npx next --version

# 2. 查看 React 实际安装版本
cat node_modules/react/package.json | grep '"version"'

# 3. 查看 next 的 CVE 是否有补丁
npm audit

5. 本项目的安全基线

  • next ≥ 16.2.6
  • react / react-dom ≥ 19.2.6
  • 禁止在生产环境使用低于 15.5.18 的 15.x 或低于 16.2.6 的 16.x。
给 AI 的提示词模板: “本项目要求 Next.js ≥ 16.2.6、React ≥ 19.2.6。请检查 package.json 和 lock 文件, 确保没有锁定旧版本;如果有同步读取 params/searchParams/cookies/headers/draftMode 的代码, 请改为异步;如果有 middleware,请评估是否需要迁移到 proxy。”
第七章 · AI 编程 PADC 流程

Plan - Act - Do - Check

PADC 是一个轻量级的 AI 协作循环。它把“让 AI 写代码”这件事分成四步, 每一步都有明确输出,防止 AI 乱改。

Plan 把需求拆成任务清单
→
Act AI 动手生成/修改代码
→
Do 运行构建和测试验证
→
Check 复盘并更新规则

Plan:计划

把模糊需求变成清单。例如“帮我优化问卷页面”要拆成:

  1. 迁移 app/ 到 src/app/,更新 tsconfig 和 tailwind 配置。
  2. 把问卷数据抽到 src/lib/data/questionnaire.ts。
  3. 把类型抽到 src/lib/types/questionnaire.ts。
  4. 把评分逻辑抽到 src/lib/utils/calculateScore.ts。
  5. 拆分组件:OptionItem、QuestionCard、QuestionSection、ResultView、Questionnaire。
  6. 每个文件顶部写教学注释。
  7. 运行 npm run build 验证。

Act:行动

AI 根据清单执行任务。关键原则是一次只做一步。 每做完一步,确认通过后再做下一步。这样出了问题,也知道是哪一步出的错。

Do:执行/验证

AI 写完代码后,必须运行验证。常见的验证有:

  • npm run build:检查 Next.js 能否正常打包。
  • npx tsc --noEmit:检查 TypeScript 类型。
  • npm run dev:在浏览器里手动点一遍。

Check:复盘

把这一轮的经验写进文档。例如:

  • 如果因为 Tailwind content 路径没更新导致样式丢失,就在 AGENTS.md 里加一条:迁移 src 时必须同步更新 tailwind.config.js。
  • 如果 AI 把客户端组件写成服务器组件,就加一条:只有 useState/onClick/浏览器 API 才加 'use client'。
PADC 用好的关键是“小步快跑”:一次改动别贪多,改完马上验证,验证完顺手把经验写进规则里。 坚持几轮下来,AI 会越用越顺手,项目质量也会越稳。
第八章 · 简单测试规范

不测试,你根本不知道 AI 有没有写歪

AI 写代码的速度很快,但快不代表对。测试的作用就是尽早揪出问题—— 不测试的话,很可能等上线了才发现页面根本打不开。

1. 版本与安全检查

npx next --version
cat node_modules/react/package.json | grep '"version"'
npm audit

先确认实际安装的版本满足安全基线:next ≥ 16.2.6、react ≥ 19.2.6。 再用 npm audit 检查依赖是否存在已知 CVE。

2. 类型检查

npx tsc --noEmit

检查 TypeScript 类型是否正确。这一步能发现很多 import 错误、字段缺失、类型不匹配。 升级到 Next.js 16 后,同步读取 params / searchParams 也会在这里报错。

3. 构建检查

npm run build

检查 Next.js 能否成功打包。16 默认使用 Turbopack,能发现路径别名、Tailwind 配置、语法、依赖等问题。 注意:Next.js 16 已移除 next lint,如需静态检查,改用 ESLint / Biome 直接运行。

4. 运行检查

npm run dev

在浏览器里打开页面,手动检查交互:

  • 单选能否正常选中
  • 多选能否正常选中和取消
  • 提交后是否跳转到结果页
  • 结果页分数是否计算正确
  • 重新填写是否清空并回到顶部

5. 何时必须运行测试

改完组件后

先运行 npx next --version 确认版本,再运行 npm run build,确保没有类型错误和语法错误。

改配置文件后

修改 tsconfig.json、tailwind.config.js、next.config.js 后,必须重新构建验证。

改数据后

改了 questionnaire.ts 后,手动点一遍,确认题目和选项显示正确。

改评分逻辑后

改了 calculateScore.ts 后,用已知的答案跑一次,确认分数符合预期。

没有测试,AI 跑得快但容易跑偏;跑几步就验证一次,才能保证方向没跑偏。
第九章 · 附录:项目目录速查

文件都放哪?

my-questionnaire/ ├── src/ │ ├── app/ # 路由层(Server Component 优先) │ │ ├── layout.tsx │ │ ├── page.tsx │ │ └── globals.css │ ├── components/ │ │ ├── ui/ # 通用 UI(Button 等) │ │ └── questionnaire/ # 问卷业务组件 │ └── lib/ │ ├── data/ # 静态数据 │ ├── types/ # TypeScript 类型 │ └── utils/ # 纯函数工具 ├── docs/ # 项目文档 │ ├── ARCHITECTURE.md │ ├── PADC-FLOW.md │ └── TESTING.md ├── scripts/ # 脚本、教程、辅助文件 ├── public/ # 静态资源 ├── README.md ├── AGENTS.md ├── next.config.js ├── tailwind.config.js ├── postcss.config.js ├── tsconfig.json └── package.json

快速判断文件归属

页面放在哪里?

src/app/xxx/page.tsx

通用组件放在哪里?

src/components/ui/

业务组件放在哪里?

src/components/questionnaire/

问卷数据放在哪里?

src/lib/data/questionnaire.ts

类型放在哪里?

src/lib/types/questionnaire.ts

计算工具放在哪里?

src/lib/utils/calculateScore.ts

版本基线:本项目使用 Next.js 16.2.6+ / React 19.2.6+。 旧版本(Next.js < 15.5.18 或 16.x < 16.2.6)存在多个 CVE,生产环境请勿使用。
记住一句话:数据归数据,UI 归 UI,状态归客户端,计算归纯函数。