从一个"房子"讲起:为什么 Next.js 是面向 AI 的全栈框架(附博客实战拆解)
这篇文章会带你从最底层的问题------"JS 和 React 到底在解决什么"------一步步走到"为什么选 Next.js",最后用一个真实的博客项目把知识点串起来。全程大白话,没有黑话。
零、先回答一个"废话问题":什么是框架?
想象盖房子。你不能每次盖房子都从烧砖 开始------那样太慢了。你需要一份已经提供好地基、墙壁和屋顶 的基本架构,你只需要关注组装和装修。
这就是框架(Framework) 。
scss
没有框架: 有框架:
┌───────────┐ ┌───────────────┐
│ 烧砖→砌墙→│ │ 地基(已打好) │
│ 装窗→铺地→│ │ 墙壁(已搭好) │
│ 装修... │ │ 屋顶(已盖好) │
│ 每次从0开始│ │ 你只负责装修 │
└───────────┘ └───────────────┘
以前,框架只服务开发者;现在,AI 也能用框架 。甚至可以说,AI 时代框架的价值被放大了:框架给了 AI 一套约束、一套上下文,AI 能更高效地根据这些约束去开发项目。
一、最底层:JS 与 React 在解决什么问题
笔记里只有三句话,但每句都值得展开:
返回 JSX 的函数,响应式状态 把开发者从低级的前端 API 命令式流水线编程,通过现代前端库 React/Vue MVVM,直接写业务就好。
1.1 命令式 vs 声明式:以前我们是怎么写页面的?
老式前端(原生 JS / jQuery)是命令式的:你要"一步一步告诉浏览器怎么做"。
ini
// 命令式:每一步都是手动操作指令
const btn = document.querySelector("#btn");
const countEl = document.querySelector("#count");
btn.addEventListener("click", () => {
const next = Number(countEl.textContent) + 1;
countEl.textContent = next; // 手动操作 DOM:读值、算值、写回
});
代码又长又脆弱------你得像流水线工人一样,盯着每一块 DOM 手动改。这就是笔记说的**"低级的前端 API 命令式流水线编程"**。
React 换了个思路------声明式:你只描述"界面长什么样",剩下的更新交给框架。
1.2 函数组件:组件就是一个"返回 JSX 的函数"
React 最核心的概念,一句话就能说清:
组件 = 一个返回 JSX 的函数。
javascript
// 函数组件:普通函数,返回 JSX
function Counter() {
return <div>我是界面的一部分</div>;
}
1.3 响应式状态:数据变了,界面自动跟着变
"响应式状态"就是------你只管改数据 ,界面自动更新,不用你手动操作 DOM。
scss
const [count, setCount] = useState(0); // 声明一个"响应式状态"
笔记里的 onCLick={setCount(count++)} 就是这回事:点击 → 改数据 → React 自动把新数字渲染到界面上。这就是 MVVM/响应式的价值:把你从"低级前端 API"里解放出来,直接写业务。
二、为什么要用框架:散乱的积木 vs 预制乐高
笔记原话:
不使用框架:散乱的积木和工具 图片放哪里?/public 页面文件放哪里?/app 组件放哪里?/components 使用框架:预制的乐高积木,提供了一系列的约束最佳实践,和 AI SDD 文档上下文不谋而合
没有框架时,每一个"东西放哪里"都要你自己做决定,而且不同人决定还不一样------这就是散乱的积木。有了框架,它用一套约定告诉你标准答案:
bash
没有框架 → 图片放哪?页面放哪?组件放哪?→ 全凭感觉
有框架 → 图片 /public 页面 /app 组件 /components → 标准答案
框架的价值 = 一套约束 + 最佳实践:
- 常见功能内置好了(图片优化、路由、字体......)
- 文件放哪里、请求方法放哪,都有约定
- 开发者只需要专注业务逻辑
关键点在这里:这套约束对 AI 格外有价值。 AI 一看到"这是 Next.js 项目",就知道代码该长什么样、文件该放哪。笔记里那句话很精辟:
AI 上下文 = 组件 + 响应式业务 + 服务器端渲染 + API
框架对开发者是"地基",对 AI 就是"说明书"。
三、为什么偏偏是 Next.js?
3.1 上下文切换成本:少学一门后端语言
传统全栈是"前端 React + 后端 Java/Python",两门语言、两套生态,来回切换,上下文切换成本很高。
scss
传统全栈: React(JS) + Java/Python ← 两套语言
Next.js : React(JS) + Next(JS) ← 一套 TS 通吃
Next.js 是基于 React 的全栈框架,服务端渲染、静态生成、API 都整合进来,一套 JS/TS 搞定。
3.2 对 AI 的支持最好
claude code / codex 这类 AI 编码工具对 Next.js 支持最好。因为它的约束明确 ,CSR/SSR 开箱即用,AI 生成的代码更不容易跑偏。
3.3 生态超级丰富
① shadcn/ui 组件库
生态里有 ElementUI、ANTD......但 shadcn/ui 是另一类东西:它不是"npm 装一下就有组件" ,而是把组件源码复制进你的项目 ,你想怎么改就怎么改。组件代码就放在 components/ui/button.tsx。
比如我们项目里用 shadcn/ui 的 Button 替换所有按钮:
javascript
import { Button } from "@/components/ui/button";
// 最普通的按钮
<Button>确定</Button>
// 变体(variant)+ 尺寸(size)
<Button variant="outline" size="sm">阅读全文 →</Button>
// 用 render 属性把它渲染成一个 <Link>,按钮就变成了链接
<Button render={<Link href="/blog/what-is-nextjs" />}>返回博客列表</Button>
一个 Button,通过 variant(default / outline / secondary / ghost / destructive / link)和 size 就能组合出各种样式,配合 tailwindcss + cva(class-variance-authority)管理。这就是"vibe coding 写组件、引入组件":AI 写组件,你引入组件,爽。
② tailwindcss:原子类名自带语义
text-sm、font-medium、bg-zinc-50......每个类名只干一件事、自带语义。AI 的语义理解能力很强,看到类名基本就能猜出样式意图,所以 tailwind 特别适合 AI 学习/生成。
③ Vercel 公司
Next.js 由 Vercel 维护,它是全球唯一一家 JS 栈的 AI coding Agent + AI 生态技术公司 。好处很实在:快捷发布、二级域名、绑定域名,开发完一键部署,不用折腾服务器。
四、实战:文件系统的路由(目录即 URL)
笔记原文:
- 文件系统的路由映射:page.tsx / layout.tsx 布局共享 / loading.tsx 加载UI / not-found.tsx 404 / error.tsx 错误UI
- 目录映射:目录名直接映射到 URL 路径
4.1 目录名直接变成 URL
这是 Next.js App Router 最大的特点:不用手写任何路由配置,建文件夹 + 建 page.tsx = 一个页面。
bash
app/
├── page.tsx → / 首页
├── about/
│ └── page.tsx → /about 关于页
└── blog/
├── page.tsx → /blog 博客列表
└── [slug]/ → /blog/xxx 博客详情(动态)
[slug] 是动态路由段 ------中括号里的名字就是 URL 里的参数。用户访问 /blog/what-is-nextjs,slug 就是 what-is-nextjs。
4.2 几个"约定文件"各司其职
| 文件名 | 作用 |
|---|---|
page.tsx |
页面内容 |
layout.tsx |
布局,所有子页面共享(导航栏就是放这里的) |
loading.tsx |
加载中的 UI |
not-found.tsx |
404 页面 |
error.tsx |
出错时的 UI |
4.3 用"三个文体要求"解读这段代码(重点)
我在生成这个项目时,提出了三个文体要求:① interface Props ② 函数组件 ③ TypeScript 类型系统。下面就用博客详情页的真实代码,把这三条讲透。
① interface / type Props:用类型约束组件参数
动态路由页面,slug 从 URL 来,Next.js 会把 URL 参数通过 params 传进来。我们用一个 Props 类型把它约束住:
typescript
// ① 定义 Props 类型:params 是 Promise,里面是 { slug }
type Props = {
params: Promise<{ slug: string }>;
};
// ② 函数组件签名:{ params }: Props ------ 参数被类型"锁死"
export default async function BlogPostPage({ params }: Props) {
const { slug } = await params; // await 一下,拿到 slug
const post = posts.find((p) => p.slug === slug);
// ...
}
为什么要写 Props 这个类型?因为类型安全 ------编译器提前检查"组件需要的参数长什么样",写错了立刻报错,不用等到运行时崩。另外注意 params 是 Promise ,所以组件里必须 await------这是 Next.js 15+ 动态路由的新约定,强制你用异步的方式读参数。
② 函数组件:组件就是"返回 JSX 的函数"
注意写法:export default function BlogPostPage(...)。没有 class、没有 this,就是普通函数 + props 解构 。这正是第一章说的"返回 JSX 的函数"。以前流行的 React.FC 写法已经过时了,现在官方推荐直接写函数 + 显式类型标注------对 AI 更友好,因为函数签名本身就是文档。
③ TS 类型系统:让数据和"接口"都可信
两篇文章的 mock 数据,用类型定义好:
ini
export type BlogPost = {
slug: string;
title: string;
date: string;
description: string;
content: string[]; // 正文按段落存成数组
};
因为 findPost 可能找不到(返回 BlogPost | undefined),所以组件里要判空 ,找不到就 notFound()。这一层类型系统,让整条数据流从头到尾都是"可信"的。
4.4 两篇文章:用 slug + mock 数据渲染
这就是需求里的"点击两篇文章,用 slug mock 数据渲染文章"。
arduino
// posts.ts ------ 两篇文章的 mock 数据
export const posts: BlogPost[] = [
{ slug: "what-is-nextjs", title: "什么是 Next.js?", content: ["...", "..."] },
{ slug: "app-router-vs-pages-router", title: "App Router 与 Pages Router", content: ["...", "..."] },
];
- 列表页 (
/blog):posts.map()渲染卡片,点卡片跳转到/blog/${post.slug} - 详情页 (
/blog/[slug]):根据 slug 在posts里find,找到就渲染正文,找不到就 404
再加上 generateStaticParams,构建时这两篇文章会被预生成成静态 HTML:
javascript
export function generateStaticParams() {
return posts.map((post) => ({ slug: post.slug }));
}
用户访问 /blog/what-is-nextjs → 直接命中构建好的静态页,秒开。流程图:
scss
/blog 列表页(静态) ──点击──▶ /blog/[slug] 详情页
│
slug 查找 mock 数据
┌────┴────┐
找到了 找不到
│ │
渲染正文 notFound() 404
五、Link 组件:不刷新页面的秘密
笔记原文:
Link 组件 ------ 它是客户端导航,无需刷新页面。(前端路由) Hash, History Router 局部刷新 还是要请求后端的,只是不整页刷新(白一下) 前端导航时,next.js 会自动发一个 RSC payload,数据是后端拿的,只是走 Ajax 请求 预加载可连接的页面,提升速度:浏览器空闲时提前下载目标页数据,"秒开"
5.1 Link 是什么
next/link 提供的 <Link>。用它做页面跳转 = 客户端导航 :点下去页面不刷新、不白屏,就像单页应用一样顺滑。
5.2 底层真相:还是要请求后端,只是"不整页刷新"
注意,Link 并不是不请求数据 ,而是不整页刷新 。点击时 Next.js 会自动发一个 RSC payload (React Server Component 的序列化数据)------数据仍然是后端给的,只是走 Ajax 请求,而不是浏览器传统导航那样整页重新加载。这跟前端路由(Hash / History Router 的局部刷新)是同一个道理。
传统导航: 点击 → 整页重新请求 → 白屏一下 → 全部重新渲染
Link 导航: 点击 → Ajax 发 RSC payload → 局部更新 → 不白屏
5.3 prefetch:浏览器空闲时提前"偷跑"
Next.js 会自动给视口内可见的 Link 加预加载:
ini
<link rel="prefetch" href="/blog" />
浏览器空闲时 就会提前下载目标页的数据,等你真正点进去时,内容已经在了 → "秒开" 。这就是资源预加载。
六、底层彩蛋:DNS 到底是个什么东西?
dns domain system key:value 分布式数据库 domain -> ip 查询(电信服务商),解析时间
这是我上课问的一句话,单独开一章讲清楚: "DNS 域名系统 = 一个 key:value 的分布式数据库" 。
6.1 你输入的域名,浏览器并不认识
浏览器真正联网靠的是 IP 地址 (一串数字)。但你输入的是 baidu.com。于是需要一位"翻译官",把域名翻译成 IP------这个过程叫域名解析。
6.2 它就是一个 key:value 数据库
数据库嘛,存的就是一条条"键值对"记录:
vbnet
key(域名) value(IP 地址)
baidu.com → 39.156.66.10
github.com → 140.82.112.3
写成 JSON 就是 { "baidu.com": "39.156.66.10" }。这就是一个 key:value 数据库------key 是域名,value 是 IP。
6.3 为什么说它是"分布式"的?
因为世界上没有一台 DNS 服务器。全球有成千上万台 DNS 服务器,分层部署、就近查询、各自缓存,防止单点故障:
arduino
用户浏览器
│ ① "baidu.com 是多少?"
▼
本地 DNS 服务器(电信/联通等运营商提供)
│ ② 本地没有缓存 → 逐级向上查
▼
根服务器 ──▶ .com 顶级域服务器 ──▶ 权威 DNS 服务器
│ ③ 查到 39.156.66.10,原路返回
▼
用户拿到 IP,开始真正访问网站
- 分布式 = 服务器遍布全球,就近服务 + 缓存,就算某台挂了还有别的顶上来。
- 解析时间 = 从"输入域名"到"拿到 IP"所花的时间。每次都现查会很慢,所以浏览器、系统、运营商都会缓存解析结果。
6.4 这跟网页性能有什么关系?------ dns-prefetch
网页性能优化里有个技巧叫 dns-prefetch:
ini
<link data-n-head="ssr" rel="dns-prefetch" href="//lf3-short.ibytedapm.com">
它的意思是: "这个域名的 IP,你提前帮我解析一下" 。等真正用到这个域名的资源时,就不用临时等解析,直接连接------省掉了解析时间 。这跟上一章说的 prefetch 殊途同归,本质都是:把将来要用的东西,提前准备好。
七、总结:一张图串起全文
vbnet
第①层 JS + React 声明式 + 响应式,把"命令式改 DOM"的痛苦解决掉
第②层 框架 散乱积木 → 预制乐高,约束 + 最佳实践,AI 也受益
第③层 Next.js React 全栈 + AI 友好 + 生态丰富
第④层 路由 目录即 URL,page/layout/loading/not-found/error
第⑤层 Link 客户端导航不白屏,RSC payload + prefetch 秒开
第⑥层 DNS key:value 分布式数据库 + dns-prefetch 提前解析
三个文体要求的落地:
| 文体要求 | 对应代码 | 一句话价值 |
|---|---|---|
| interface Props | type Props = { params: Promise<{slug: string}> } |
类型安全,写错就报错 |
| 函数组件 | export default function BlogPostPage({ params }: Props) |
组件就是返回 JSX 的函数,签名即文档 |
| TS 类型系统 | type BlogPost = {...} / generateStaticParams |
数据流全程可信,AI 更好理解 |
一句话收尾:框架解决的不是"会不会写代码",而是"代码往哪放、怎么写才对"------对人如此,对 AI 更是如此。 这就是 Next.js 在 AI 时代成为"全栈默认选择"的原因。
如果你动手跟着做,推荐顺序是:npx create-next-app 建项目 → 搭 /blog 列表页 → 加 /blog/[slug] 详情页 → 用 shadcn/ui 的 Button 统一按钮 → 玩一玩 Link 的 prefetch。每一步都对应上面某一个小节,学完你也就拥有了一个能部署的真博客。
(说明:文中流程图为 ASCII 示意图,掘金里同样正常显示;如果想换成更精美的配图,可以按图里的结构自行绘制上传。)
文章就到这里。如果你需要我:调整篇幅(太长/太短)、改成更口语化或更硬核的风格、补充某个小节(比如 shadcn/ui 的 cva 原理、RSC 更深的序列化细节),或者把某个 ASCII 图换成文字步骤清单,直接告诉我就行。