前端名词地图:AI 编程时它到底在说什么
不教你写前端。教你听懂 AI 嘴里的那一串词——它在说哪一层、为什么现在要动它、哪几个词你可以先不管
📅 更新于 2026-09-13
本文校对于 2026-09-13 的版本快照:React 19.3 · Next.js 16.3.5 · Vite 8.3 · Tailwind CSS 4.3 · Node.js 24.21 LTS(Krypton)· TypeScript 7.0.2 · 官方源:react.dev / nextjs.org/docs / MDN
0030 秒速览:三句话,然后决定你从哪一节开始
当 AI 对你说「这是个客户端组件(需要在浏览器里跑的那种组件,§06 讲),加个 use client,然后重新 build 一下(把源码加工成浏览器能跑的文件,§05 讲),注意别触发 hydration mismatch」时,你能听懂这句话里每一个词分别属于哪一层、它要你做什么、以及哪一句值得你停下来追问。你不需要因此会写前端代码——你需要的是不再被术语挡住判断。
如果只记三句话
- 前端 = 在访客的浏览器里跑的那一半。后端 = 在服务器上跑的那一半。凡是"别人一打开网页就该发生的事",都属于前端。所以前端的问题,往往只有"换台电脑 / 换个网络 / 换个浏览器"才暴露出来。
- AI 嘴里的前端词,九成落在四层里:内容(HTML)、样式(CSS)、交互(JavaScript 与框架)、工具链(Node / npm / 打包)。这四层再套上外面那一层"交付"(部署、域名、缓存、跨域),就是本文的整张地图。听到一个生词,第一反应不该是"这什么",而是"它属于哪一层"——定层之后,你基本能猜出它大概管什么。
- 看见页面 ≠ 页面能用。服务器先把一份"死的"页面发到浏览器(这就是 SSR / 静态生成在干的事,§08 讲),浏览器再给它装上"活"的能力(这一步叫水合)。所以会出现"页面明明显示了,按钮就是点不动"这种现象——它有个专门的报错名字,叫 hydration mismatch。
一张表:你现在的处境 → 该看哪几节
| 你现在的状态 | 先看这几节 | 为什么是这几节 |
|---|---|---|
AI 甩给我一堆命令(npm install、npm run dev),我不敢敲,也不知道敲完会发生什么 | 03 → 04 → 05 | 先用三个文件跑出你自己的第一个网页,再理解"装依赖"和"起服务"这两件事,之后 AI 给的命令你至少知道它在动什么 |
我看到报错里有 Cannot find module、npm ERR!、EACCES,完全不知道从哪下手 | 04-6(报错速查表)→ 05-6 | 这一类报错九成不是"代码写错了",而是依赖没装、装错地方、Node 版本太低——都不是靠改代码能修的 |
AI 说"这是客户端组件""加个 use client""改成 async 组件",我点头但其实不懂 | 06 → 08-7 | 组件、props、state 是 React 的最小词汇表;服务端 / 客户端组件的分界是 Next.js 这一代的必答题 |
| 页面在我电脑上是好的,发给别人就打不开 | 10-1 → 10-2 | 九成是因为你打开的是 localhost——那台"服务器"就是你自己,别人连不上 |
AI 往 className 里塞了一长串英文(flex items-center gap-2 rounded-lg),我不敢动 | 09-3 → 09-4 | 那是 Tailwind 的工具类,一长串其实是几条独立的样式声明,拆开看就不吓人了 |
| 我只想知道某个词是什么意思,不想读整篇 | 13(名词速查表) | 按层分成四张表,每行都写了"什么时候你必须懂、别当什么用" |
| 我想知道 SSR、CSR、水合到底哪个好,AI 每次说得都不一样 | 08 | 它们不是"好坏的排序",而是三种不同的取舍;看完那张六词对照表,你就能自己判断 AI 有没有在乱说 |
本文校对的版本快照(你可以自己复核)
下面这些数字不是从记忆里写的,是写作当天从官方源读出来的。你可以在自己的终端里跑同样一条命令来验证——这也顺便是你第一次用上"包管理器"这个东西(§04 会讲):
npm view react version next version vite version tailwindcss version typescript version
node -v # 参考:本文写作时 LTS 是 v24.21.0,最新是 v26.8.2
| 工具 | 写作当天的版本 | 它属于哪一层 / 你什么时候会撞见它 |
|---|---|---|
| Node.js | 24.21 LTS(Long Term Support,长期支持版:官方会持续修安全问题的那个档位,新项目该选它;"Krypton" 是它的代号),最新线 26.8 | 工具链底座。只要 AI 让你敲 npm 开头的命令,就用到它 |
| npm | 随 Node 一起装 | 装包的默认工具。包管理器还有 pnpm / yarn / bun |
| React | 19.3 | 交互层最流行的"库"。组件、props、state、hook 都出自这里 |
| Next.js | 16.3.5 | 盖在 React 上的"框架"。SSR / SSG / 水合这些词最常在这里出现 |
| Vite | 8.3 | 本地开发服务器 + 打包工具。跑 npm run dev 时在后面干活的多半是它 |
| Tailwind CSS | 4.3 | 样式层的一种写法:把样式拆成一个个小类名 |
| TypeScript | 7.0.2 | 给 JavaScript 加"类型检查"的语言。文件后缀 .ts / .tsx 就是它 |
上面这张表半年后就会不准(npm view 那条命令永远能拿到最新的)。但本文真正要给你的东西——哪些词属于哪一层、哪几个词最容易混、听到生词时怎么反问——是概念,不会因为版本号变了而失效。本文的时效标注是"演进型",意思是版本号这类东西需要定期对账重核,而讲解部分不用重写。
01「前端」到底是哪一半:一次网页访问的全过程
你其实每天都在使用"前端",只是没这么叫它
你打开一个购物网站,看到商品图片、点进详情页、在搜索框里打字、点"加入购物车"、页面右上角数字变成 1——这一连串体验,全是前端在干活。前端的工作只有一句话:把数据变成你眼睛能看到、手指能点的界面,并且在你的浏览器里当场响应你。
"前端"里的"前",指的不是技术难度,也不是时间先后,而是离用户更近的那一端。与之相对的"后端",是离数据更近的那一端。服务器就是那台一直开着、专门回应别人请求的电脑——它可能在你房间的机柜里,也可能是云上租来的一台机器。
四个词各自管什么
| 词 | 一句话 | 跑在哪台机器上 | 它出问题时的典型症状 |
|---|---|---|---|
| 前端(frontend,也叫客户端 / 浏览器端 / client-side) | 把数据画成界面,接住你的点击和输入 | 访客自己的电脑、手机 | 排版错乱、按钮点不动、手机上显示不下、只有某个浏览器出问题 |
| 后端(backend,也叫服务端 / server-side) | 存数据、算规则、判断"这个人能不能做这件事" | 你自己或云上的服务器 | 所有人都登不上、数据算错、接口返回 500 错误(500 是"服务器内部出错"的标准编号) |
| 数据库(database) | 把数据长期存起来,下次还能查到 | 服务器上(或云数据库服务) | 数据丢了、变慢、重复写入 |
| 部署(deploy) | 把你写好的东西放到一台别人能访问的机器上 | 云平台 | 你本地一切正常,但打开网址是 404 或白屏 |
补两个你一定会遇到的相关词:API(Application Programming Interface,接口)是前后端之间的"点菜窗口"——前端按约定格式递过去一个请求,后端按约定格式回一份数据;全栈(full-stack)不是一种技术,而是"前端后端都能自己写"的人或项目。
从你在地址栏敲下网址,到看见页面,中间发生了什么
这六步里,第 3~6 步基本都在前端的地盘上。理解了这条链,很多术语就能自动归位:HTML / CSS / JavaScript 管第 4 步,构建工具管"为什么源码不能直接用、要先生成这些文件",SSR / SSG 管第 3 步的 HTML 是谁在什么时候生成的,水合管第 5 步。
"客户端 / 浏览器端 / 前端"是同一件事吗
基本是,但语气不同:
- 前端:强调"这是项目里的一块工作",说人、说岗位、说技术栈时用它。
- 客户端 / 浏览器端:强调"这段代码在哪台机器上跑"。AI 说"这段只能在客户端跑",意思是它依赖浏览器才有的能力(比如读取你点的位置、访问摄像头、读写本地存储)。
- 服务端 / 服务器端:反方向,指这段代码在服务器上执行,你访客的浏览器里根本没有它。
为什么这个区分值钱?因为前端代码是"公开"的。任何访客都能按 F12 看到你发到浏览器的所有内容。所以有一类错误是致命的:把数据库密码、付费接口的密钥写进了前端代码。§10-4 会专门讲这件事。
前端问题的经典特征是只在某些人的机器上出现:同事的电脑上正常、手机上错位、某个浏览器白屏。后端问题的经典特征是所有人都一样地坏。遇到问题时先做这个判断,能省掉一半的瞎改——你不需要看懂代码,只需要问一句"这是所有人都这样,还是只有我这样?"
02一个网页只需要三种文件:HTML / CSS / JavaScript
骨骼、皮肤、肌肉
三种文件各自负责一件事,这个分工从 1990 年代到今天没变过,所有前端框架最后都得回到这三样东西上:
| 文件类型 | 它管什么 | 身体的类比 | 它单独存在时会怎样 |
|---|---|---|---|
| HTML(HyperText Markup Language,超文本标记语言) | 内容与结构:这里有个标题、那里有张图片、下面是个按钮 | 骨骼:决定有什么、长在哪 | 网页能打开,但像一份 1995 年的文档,全是黑字白底 |
| CSS(Cascading Style Sheets,层叠样式表) | 样式与排版:颜色、字号、间距、圆角、手机上和电脑上怎么排 | 皮肤:决定好不好看 | 结构还在,但没有颜色和排版,全部元素从上往下一列堆着 |
| JavaScript(常缩写为 JS) | 行为:点击后发生什么、输入后怎么校验、数据怎么加载 | 肌肉:决定能不能动 | 页面能看不能动,按钮点了没反应 |
HTML 里的"标记"写成一对尖括号包起来的标签(tag),比如 <h1>标题</h1>;标签加上它包住的内容合起来叫一个元素(element);写在标签里、给元素补充信息的键值对叫属性(attribute),比如 <a href="https://example.com"> 里的 href 就是"链接指向哪里"这个属性。
这个类比到哪里就不准了
类比必须说边界,否则你会用它推出错的结论:
- 三者会互相渗入。JavaScript 可以改样式(改颜色、改位置),CSS 也能做出"动"的效果(鼠标悬停变色、展开收起),HTML 里甚至能写样式。它们不是三层互不打扰的皮,而是三种职责。
- 现代框架会把三者写进同一个文件。React 的组件文件里,长相(HTML 样子的 JSX)、逻辑(JS)、样式(类名或 CSS 对象)常常写在一起。这时"三种文件"变成"三种职责",而不是三个物理文件。
- CSS 不是"可选的美化"。手机屏幕上能不能看清、按钮够不够大能点到,这些是可用性问题,不是审美问题。
后缀名认亲:AI 生成的文件到底在写哪种语言
| 后缀 | 是什么 | AI 为什么用它 / 你该怎么看它 |
|---|---|---|
.html | 纯 HTML 页面 | 最早的形态。单独一个就能双击打开 |
.css | 纯样式表 | 被 HTML 引用;也常被框架的入口文件一次性引入 |
.js | 纯 JavaScript | 浏览器直接能跑 |
.jsx | JavaScript + 类 HTML 写法(JSX) | 写 React 用。浏览器看不懂,需要先"翻译"(这就是构建,见 §05) |
.ts | TypeScript:给 JavaScript 加了"类型"检查 | 浏览器同样看不懂,需要翻译。类型就是给每个数据贴上标签(这是字符串、这是数字),写错时提前报错 |
.tsx | TypeScript + JSX | React + TypeScript 项目的默认后缀,AI 生成现代前端项目时最常见 |
.vue / .svelte | 另外两个框架(Vue、Svelte)的单文件格式 | 长相和能力都类似 .tsx:一个文件里装下一个组件的长相、逻辑、样式 |
.json | 纯数据格式,键值对 | package.json 这类配置文件就是它;本身不含任何逻辑 |
看到 .ts / .jsx / .vue 这类后缀,你就知道这个项目必须要有一台"翻译机"才能跑,也就必然会有 package.json、必然要先 npm install。看到纯 .html,它可能双击就能跑。这条判断能让你在打开一个陌生项目时先知道"我要不要先装东西"。
03最小例子:三个文件,不装任何东西就能跑
这一节请真的动手做一遍,五分钟。做完你会同时获得三样东西:一个能跑的网页、"三种语言各管什么"的手感、以及后面所有术语的落脚点。
my-first-page用你顺手的任何方式:Finder 里右键新建文件夹、VS Code 里"打开文件夹"、或者终端里 mkdir my-first-page && cd my-first-page。没有"必须用哪个工具"这回事。
index.html,把下面整段粘进去<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>我的第一个网页</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>你好,这是我做的网页</h1>
<p id="count">你已经点了 0 次</p>
<button id="btn">点我</button>
<script src="app.js"></script>
</body>
</html>
这份文件干了两件"牵线"的事:第 7 行把 style.css(样式)拉了进来;倒数第 3 行把 app.js(行为)拉了进来。所以接下来那两个文件的名字不能拼错,否则网页还能打开,但样式和点击会静默失效——这是新手第一次遇到的"我明明写了但没生效"。
style.css(管长相)body {
font-family: system-ui, sans-serif;
background: #0f1117;
color: #e4e6ef;
display: flex; /* 让子元素排成一行,居中 */
flex-direction: column;
align-items: center;
padding: 48px;
}
button {
font-size: 18px;
padding: 10px 20px;
border-radius: 8px;
border: none;
background: #6366f1;
color: white;
}
button:hover { background: #4f46e5; } /* 鼠标移上去变深一点 */
app.js(管行为)let n = 0;
const btn = document.getElementById("btn");
const label = document.getElementById("count");
btn.addEventListener("click", () => {
n = n + 1;
label.textContent = `你已经点了 ${n} 次`;
});
这 6 行代码把三个后面会反复出现的词一次用掉了:document.getElementById 是在从 DOM 里找元素(§07);addEventListener("click", ...) 是挂一个事件监听(§07);后面那个函数体,是浏览器在每次点击时替你调用的回调。
index.html,看它跑起来页面应该是深色背景、中间一行标题、一个紫色按钮。点一下按钮,下面那行字从 0 变成 1。这就是一个完整的网页了——没有 Node、没有 npm、没有框架。
① 把 style.css 里 background: #0f1117 改成 background: #ffffff,刷新 → 背景变白(CSS 决定长相)。
② 把 index.html 里 <h1> 那行文字改掉,刷新 → 标题变了(HTML 决定内容)。
③ 把 app.js 里 n + 1 改成 n + 10,刷新再点 → 一次跳 10(JS 决定行为)。
从"我电脑上能跑"到"别人能看到",还差四步
上面这个网页有个致命特点:只有你能看。它躺在一个 file:// 开头的地址上,访问的是你硬盘里的文件。要让别人访问,需要走完这条链——后面每一节都在拆这链上的一环:
| 还差的步骤 | 为什么需要它 | 在哪一节 |
|---|---|---|
| 装 Node.js,用包管理器装依赖 | 你要用框架、用现成的轮子,就需要一个统一的"装东西"的地方 | §04 |
| 起一个本地开发服务器(dev server) | 自动刷新、报错定位、模拟真实环境;端口就像 localhost:5173 | §05 |
| 构建(build)出上线用的成品 | 浏览器看不懂 JSX / TypeScript / 一堆模块,要先翻译压缩成它能跑的文件 | §05 |
| 部署(deploy)到一台别人能访问的机器 | 把你的成品放到云上,并绑定一个域名 | §10 |
无论后面 AI 给你生成多复杂的项目,剥到最后都是这三样东西:内容、样式、行为。框架只是把这三样组织起来的一种写法,不是第四种语言。当你被一堆名词绕晕时,回到这一节问自己:AI 现在动的,是这三样里的哪一样?
04npm install 到底装了什么:包、依赖、node_modules
先解决一个疑问:写网页为什么要装 Node.js
JavaScript 原本只活在浏览器里。Node.js 做的事,是把 JavaScript 从浏览器里搬出来,装进你的电脑,让它能像 Python 一样在命令行跑。官方对它的定义是 "an asynchronous event-driven JavaScript runtime"——异步、事件驱动、JavaScript 运行环境;翻译成人话:一个能跑 JavaScript 的程序;"异步、事件驱动"说的是它的做事方式——不傻等一个任务做完,而是一边等结果一边接下一件活。这里的运行环境(runtime)指"负责执行你这份代码的那个东西":浏览器是 JavaScript 的一种运行环境,Node.js 是另一种。
那它跟写网页有什么关系?因为现代前端开发的所有工具(构建、打包、代码检查、本地服务器)都是用 JavaScript 写的程序,必须靠 Node.js 才能运行。所以"装 Node"不是学一门新语言,而是装一个能让工具跑起来的底座。
验证装没装、装得够不够新:
node -v # 本文写作时 LTS 是 v24.x,最低要求一般是 20.9+
npm -v # npm 随 Node 一起装好,不需要单独装
五个词一次讲清
这一组词是新手最先被砸晕的地方,因为 AI 会在同一段话里同时用到它们。它们其实是同一件事的五个侧面:
| 词 | 一句话 | 它在哪 | 什么时候你必须关心它 |
|---|---|---|---|
| 包(package,也叫"库"或"模块") | 别人写好、可以直接拿来用的一坨代码 | 网络上(包注册中心 npm registry) | AI 说"装一个 xxx"时,说的就是这个 |
| 依赖(dependency) | 你的项目"依赖"了哪些包——也就是别人写的代码清单 | 写在 package.json 里 | 换电脑、部署时,靠它把同样的一批包重新装回来 |
package.json | 项目的身份证 + 依赖清单 + 常用命令的快捷方式 | 项目根目录 | 拿到任何前端项目,第一眼看它,就知道这是什么项目、装了什么、怎么跑 |
package-lock.json | "锁"住每个包的确切版本号,保证每台机器装出一模一样的结果 | 项目根目录(自动生成) | 它和 package.json 不一致时,说明有人改了依赖没重装;这个文件要提交进 git(git 是管理代码版本的工具;这一条说的是"要交给它保管") |
node_modules | 真正被下载下来的那些包,一个包一个文件夹,堆在这里 | 项目根目录(自动生成) | 它很大(几百 MB 到 1 GB 都正常)、永远不进 git、但它不能随手删——删了项目就跑不起来,得重新装 |
把这五个词串成一句完整的话,AI 常说的那句就通了:
「你这个项目依赖没装,先 npm install 一下,然后 npm run dev」=「根据 package.json 里那份依赖清单,去网上把每个包下载到 node_modules 里(这一步就是 install),装完之后按清单里定义好的脚本 dev,把本地开发服务器起起来。」
你实际会敲的命令
下面每条都可以直接复制。注意最后两条是"修复类"命令,遇到怪问题时先跑它们,比改代码有效得多。
# 1. 拿到一个已有项目后的第一步:按清单装齐所有依赖
npm install
# 2. 装一个包(会自动记进 package.json 的 dependencies)
npm install tailwindcss
# 3. 装一个"只在开发时用、上线不需要"的包(记进 devDependencies)
npm install -D typescript
# 4. 看 package.json 里定义了哪些命令(AI 让你 npm run xxx 时,xxx 就在这)
npm run
# 5. 不安装、直接用一次某个命令行工具(比如建一个新项目)
npx create-next-app@latest my-app
# 6. 修复类①:依赖装坏了、报错莫名其妙 —— 删掉重装
rm -rf node_modules package-lock.json && npm install
# 7. 修复类②:确认自己用的是哪个 Node(版本太低是无数怪报错的根因)
node -v
第 5 条里的 npx 值得单独说:npm 是"装到项目里",npx 是"临时借来跑一次"。建新项目这种一辈子跑一次的事,用 npx 更干净,不会在硬盘上留一堆用不到的包。
npm、npx、pnpm、yarn、bun:五个"跑命令的家伙"差在哪
| 名字 | 它是什么 | 和 npm 的关系 | 你该用哪个 |
|---|---|---|---|
| npm | Node 自带的包管理器,最通用 | 基准 | 没特别理由就用它,教程都以它为准 |
| npx | npm 附带的"临时执行器" | 不是替代品,是补充 | 跑一次性的工具命令 |
| pnpm | 第三方包管理器,装得快、硬盘占用小(多个项目共用同一份包) | 用同样的 package.json,只是安装方式不同 | 磁盘紧张或项目多时值得用;命令几乎一样(pnpm install / pnpm dev) |
| yarn | 第三方包管理器,曾经比 npm 快 | 同上 | 只在别人给的项目明确说用 yarn 时跟着用 |
| bun | 新的 JavaScript 运行环境 + 包管理器,主打快 | 同时替代 Node 和 npm 的一部分角色 | 尝鲜可以;生产项目先跟 AI 确认兼容性 |
npm、pnpm、yarn 会生成不同的 lock 文件(package-lock.json / pnpm-lock.yaml / yarn.lock)。同一个项目里混用,会出现"我装了但报找不到包"这类灵异现象。判断方法很简单:看项目根目录里已经有哪个 lock 文件,就用哪个工具。如果 AI 让你跑 pnpm install 而目录里只有 package-lock.json,先问它一句是不是要换工具。
一个真实的 package.json 长什么样
下面这份是照抄就能用的最小形态,每一行我都标了它是干嘛的:
{
"name": "my-first-app", // 项目名,小写、不能有空格
"private": true, // 防止误发布到公共仓库
"type": "module", // 用现代 import/export 写法
"scripts": {
"dev": "vite", // npm run dev 起开发服务器
"build": "vite build", // npm run build 生成上线成品
"preview": "vite preview" // 本地预览 build 出来的成品
},
"dependencies": {
"react": "^19.3.0" // 运行时要用的包
},
"devDependencies": {
"vite": "^8.3.0",
"typescript": "^7.0.2" // 只在开发和构建时用
}
}
版本号前面那个 ^ 是语义化版本(semver)的写法:19.3.0 由"大版本.小版本.补丁版本"三段组成,^19.3.0 的意思是"允许自动升到 19 开头的任何更高版本,但不要跳到 20"。大版本升级通常意味着有东西被改坏了,这正是 package-lock.json 存在的意义——它把实际装的版本钉死,防止"昨天还好好的,今天装出来就崩了"。
排错速查:这一类报错九成和代码无关
| 报错里出现这些字样 | 它在说什么 | 你该做什么 |
|---|---|---|
Cannot find module 'xxx' / Module not found | 代码想用一个包,但 node_modules 里没有 | 先 npm install;还不行就删掉 node_modules 重装 |
npm ERR! ERESOLVE / peer dep | 两个包要求的版本互相冲突 | 不要直接 --force。先问 AI:「这两个包的版本冲突,请给出兼容的组合,不要用 --force」 |
EACCES / permission denied | 权限不够,通常是以前用管理员权限装过东西 | 问 AI 要"不用 sudo 的修复方式",不要习惯性加 sudo |
command not found: node | Node 没装,或装了但终端找不到(装完要重开终端) | 去 nodejs.org 装 LTS 版,然后重开终端 |
You are using Node.js vX. vite requires Node.js ... | Node 版本太低 | 升 Node 到该工具要求的版本(Vite 8 要求 20.19+ 或 22.12+) |
'vite' 不是内部或外部命令 / command not found: vite | 那个命令不是全局命令,要靠 npm run dev 间接调用 | 用 npm run dev,不要直接敲 vite |
ENOENT: no such file or directory, open '.../package.json' | 你在错误的目录里敲命令 | cd 到有 package.json 的那一层(通常是最外层文件夹) |
这张表的价值在于:这一整类报错都不需要你看懂前端代码,它们说的是"环境没搭好"。新手最大的浪费就是把它们当成"我代码写错了",然后开始瞎改业务逻辑。
05「构建」到底在构建什么:dev server、打包、上线产物
为什么源码不能直接扔给浏览器
回到 §02 那张后缀表:.tsx、.ts、.vue 浏览器都不认识;一个项目里成百上千个文件互相引用(这叫模块,用 import / export 互相引用),浏览器一个个去要会非常慢;Tailwind 那串类名也需要被翻译成真正的 CSS。
所以现代前端有一条铁律:你写的是"源码",浏览器跑的是"产物",这两者之间必须有一道加工工序,这道工序就叫构建(build,也叫打包 / bundle)。负责这件事的程序,叫构建工具(bundler)。
dev server:为什么 AI 总要你开着一个终端窗口不动
开发服务器(dev server)是构建工具在你电脑上临时开的一个小网站。它做三件事:
- 实时加工:你一保存文件,它立刻在内存里翻译好,浏览器一刷新就是最新版。
- 给个地址:通常是
http://localhost:5173(Vite 默认)或http://localhost:3000(Next.js 默认)。localhost的意思是"这台机器自己"。 - 报错更友好:出错时它在页面上盖一层红色提示,比浏览器控制台里一行天书清楚得多。
那个终端窗口不能关——关了服务器就停了,网页立刻打不开。这就是"为什么 AI 让我开两个终端"的答案:一个跑 dev server(一直占着),另一个敲别的命令。
其中最常被提到的能力叫 HMR(Hot Module Replacement,热模块替换):你改了一行样式,页面不用整页刷新,那一块自己就变了,表单里填了一半的内容也不会丢。
构建产物里的四个词
| 词 | 一句话 | 它为什么存在 |
|---|---|---|
| 打包(bundling) | 把几百个源码文件合并成浏览器容易下载的少数几个文件 | 减少请求次数,加载更快 |
| 压缩(minify) | 删掉空格、注释,把变量名改短 | 让文件更小。压缩后的代码不能读,所以别拿它来改 bug |
| 摇树(tree-shaking) | 你没用到的那些代码,打包时自动丢掉 | 比如你只用了某个库的一个函数,不该把整个库都塞给访客 |
| 源码映射(source map) | 一张"压缩后的第 3 行第 800 列,对应源码第 42 行"的对照表 | 让浏览器报错时能指回你的源码,否则你看到的错误位置毫无意义 |
产物放在哪?Vite 项目默认是 dist/ 文件夹,Next.js 是 .next/。部署时真正被传上服务器的就是产物,不是你写的源码——这一点很关键:所以"我明明改了源码,线上怎么没变"的常见原因就是没有重新构建、重新部署。
Vite 是什么,和 webpack 差在哪
Vite(法语"快"的意思,读作"veet")是目前新项目里最主流的构建工具,最新是 8.3。它的官方说明写得很直白:它由两部分组成——一个本地开发服务器,和一个打包命令。它相对老牌工具 webpack 的核心差别是:
| webpack(老牌) | Vite(新一代) | |
|---|---|---|
| 启动开发服务器时 | 先把整个项目打包一遍,项目越大等得越久 | 几乎不打包,直接用浏览器原生支持的模块加载方式,秒开 |
| 改一行代码后 | 重新打包受影响的部分 | 只把那一小块换掉,几乎无感(HMR) |
| 打包成上线产物时 | webpack 自己打包 | 交给打包器完成(Vite 8 用的是 Rolldown) |
| 你该怎么对待它 | 老项目里会遇到,配置复杂 | 新项目默认就是它;配置文件 vite.config.ts |
还有两个你在 Next.js 里会看到的同类词:Turbopack 是 Next.js 官方的构建工具(Next.js 16 的发布说明把它列为"所有应用的默认打包器",官方称构建快 2–5 倍),Rolldown 是 Vite 8 用的打包器。它们是同一层的不同实现——看到这些名字,你只需要知道"它们是那一层的工具",不必记住区别。
最容易混的一张表:dev 与 build
npm run dev(开发模式) | npm run build(生产构建) | |
|---|---|---|
| 它产出什么 | 一个本地服务器,边改边看 | 一堆静态文件(dist/ 或 .next/) |
| 谁能访问 | 只有你自己这台电脑 | 把这些文件放上服务器后,全世界 |
| 速度 | 快,追求"改完立刻看到" | 慢,追求"打开最快、体积最小" |
| 报错严格程度 | 宽松,有些问题它不拦 | 严格,很多"dev 能跑"的代码会在这里失败 |
| 上线的依据 | ❌ 不能作为"做好了"的证据 | ✅ 只有 build 过了才算真的过了 |
| 常见误用 | 把 dev 的地址发给别人 | 改了代码忘了重新 build / 重新部署 |
dev 是给你自己看的,build 是给全世界看的。所以每次准备"发给别人"或"部署"之前,都要先跑一次 npm run build,并在本地用 npm run preview(Vite)或 npm run start(Next.js)模拟一次真实运行。这条习惯能提前拦掉一大半"上线才发现的问题"。
06组件、props、state、hook:React 那一套词
库和框架:一句分界
AI 会说"React 是库,Next.js 是框架",这不是抠字眼:
- 库(library):你调用它的功能。React 就属于这一类——它提供"把界面拆成组件"的能力,但路由、取数据、打包,它都不管,你自己选。
- 框架(framework):它调用你的代码。Next.js 属于这一类——它规定文件放在哪个文件夹、页面怎么写、什么时候渲染,你按它的规矩填内容。
所以"React 项目"和"Next.js 项目"的关系是:Next.js 是一个把 React 当零件用的框架(官网自称 "The React Framework")。这也解释了一个常见困惑:为什么同样写 React,教程里的代码长得不一样——它们用的是不同的框架(Next.js / Remix / 纯 Vite + React)。
组件:把页面切成能被复用的块
组件(component)是 React 世界的第一概念,一句话:一个"返回界面长相"的函数。你给它取个名字,它就变成一个可以反复使用的标签。
// 定义一个组件:名字首字母必须大写,这是 React 认人的方式
function Greeting({ name }) {
return <h1>你好,{name}</h1>;
}
// 使用它 —— 用起来就像一个自己发明的 HTML 标签
<Greeting name="小明" />
<Greeting name="小红" />
为什么值得多这一层?因为页面里"重复出现的东西"太多了:按钮、卡片、导航栏、列表中的每一行。组件让你只写一次,改一处就全都变了。当你听到 AI 说"我把它抽成一个组件",意思就是"这段重复的长相和逻辑,我要给它取个名字收起来"。
JSX / TSX:把 HTML 写进 JavaScript 里
上面那段代码里出现的 <h1>你好,{name}</h1> 既不是 HTML 也不是字符串,而是一种叫 JSX 的写法(TypeScript 版本叫 TSX):长得像 HTML,但里面可以用 {} 插入 JavaScript 表达式。它不能直接被浏览器执行,必须先被构建工具翻译成普通的 JavaScript 函数调用——这正是 §05 里"必须要有构建"的第一大原因。
两个你现在就该知道的小差别:JSX 里写样式类名要用 className(不是 class),闭合标签必须写全(<img /> 不能省那个斜杠)。AI 生成的代码里出现它们时,不是什么特殊语法,只是 JSX 的规矩。
props 和 state:外面递进来的 vs 自己记着的
这两个词是 React 最核心的一对,也是最容易混的一对。分界只有一条:props 是别人给的,state 是自己记的。
| props(属性) | state(状态) | |
|---|---|---|
| 从哪来 | 父组件在标签上传进来的,比如 <Greeting name="小明" /> 里的 name | 组件自己用 useState 声明的 |
| 能不能改 | 不能。props 是只读的,子组件只能看 | 能改,但必须用配套的更新函数去改 |
| 改了会怎样 | 父组件重新给值 → 子组件跟着重新渲染 | 调用更新函数 → 这个组件重新渲染 |
| 典型例子 | 用户名、主题色、一篇文章的数据 | 输入框里现在的内容、计数器、弹窗开没开 |
| 混了会怎样 | 把该用 state 的东西写成普通变量:你改了它,界面不会变。这是新手第一个"我明明赋值了却不生效"的坑 | |
function Counter() {
const [n, setN] = useState(0); // state:初始值 0,setN 是唯一的改法
return (
<button onClick={() => setN(n + 1)}>
点了 {n} 次
</button>
);
}
hook:以 use 开头的函数是什么
hook(钩子)是 React 提供的一批特殊函数,命名都以 use 开头。它们的共同作用是:把一个"外挂能力"接进组件里。你已经见过 useState(给组件一份记忆)。最常出现的另外两个:
| hook | 它给组件接上什么能力 | 什么时候 AI 会用它 |
|---|---|---|
useState | 记忆:记住一个会变的值 | 输入框内容、开关状态、计数器 |
useEffect | 副作用:在"渲染完之后"干一件有外部影响的事 | 页面打开时去请求数据、定时器、监听窗口大小 |
useRef | 一个"不会引起重新渲染"的盒子,常用来直接抓住某个 DOM 元素 | 让输入框自动获得焦点、接入第三方图表库 |
自定义 hook(如 useUser) | 把一段重复的 hook 逻辑打包,起个名字复用 | 多个页面都要"取当前登录用户"时 |
hook 有两条铁律,违反了会报错,而 AI 偶尔会犯:① 只能在组件函数(或其他 hook)的最外层调用,不能放进 if、循环或普通函数里;② 必须以 use 开头命名。你不需要背,只需要在报错里看到 React Hook "useXxx" is called conditionally 时知道:这是"hook 被放进了条件判断里",不是玄学问题。
"重新渲染"不是刷新页面
AI 说"这个组件会重新渲染(re-render)",很多人会理解成"页面重新加载"。不是:
注意第 4 步——页面不会整页重画,你输入框里没提交的内容也不会丢。这也是为什么"重新渲染"听起来吓人、实际通常只是一小块变了一下。至于第 4 步里"怎么对比"的技术手段,就是 §07 要讲的虚拟 DOM。
一个能跑的最小 React 项目
下面三条命令建出一个真实可跑的 React + TypeScript 项目。这是 AI 最常让你敲的一组:
# 1. 生成项目(npx = 临时借工具用一次,见 §04)
npm create vite@latest my-app -- --template react-ts
# 2. 进目录、装依赖
cd my-app
npm install
# 3. 起本地开发服务器(这个终端窗口要一直开着)
npm run dev
把 src/App.tsx 的内容换成下面这段,你就在真项目里用上了组件、state 和事件:
import { useState } from "react";
function App() {
const [n, setN] = useState(0);
return (
<div style={{ padding: 40 }}>
<h1>你已经点了 {n} 次</h1>
<button onClick={() => setN(n + 1)}>点我</button>
</div>
);
}
export default App;
对比 §03 那 6 行原生 JavaScript:那里你手动找到按钮、挂监听、再手动改文字;这里你只改了一个数字 n,界面自己跟着变。这个差别,就是"命令式"与"声明式"的分水岭——§07 会专门讲它。
易混速查(跨层的总表在 §12)
| 长得像 | 分界一句话 |
|---|---|
| state vs 普通变量 | 普通变量改了界面不会变;state 改了会触发重新渲染 |
| 组件 vs 普通函数 | 返回界面、名字首字母大写的才是组件;不然只是普通函数 |
| JSX vs HTML | 长得像,但 className、闭合标签、{} 插值都不同,且必须被构建翻译 |
07DOM、事件、虚拟 DOM:页面是怎么"动"起来的
DOM 是什么:浏览器把 HTML 变成一棵可以改的树
MDN(Mozilla 维护的 Web 技术文档站,前端最权威的免费参考书)对 DOM 的说明是:它"把文档的结构——比如表示网页的 HTML——在内存里表示出来",而且"用一棵逻辑树来表示一个文档"。翻译成人话:
浏览器读完 HTML 之后,不会只把它当一段文字存着,而是把它变成一个住在内存里的树状结构,每个标签是一个节点。你在页面上看到的一切,都是这棵树被画出来的样子。JavaScript 之所以能改页面,是因为它能改这棵树——改了树,浏览器就重画。
所以你会在报错和 AI 的话里反复看到这些词:节点 / 元素(树上的一个个标签)、document(整棵树的入口)、document.getElementById(按身份证号找节点,就是 §03 里用的那个)。F12 打开开发者工具,Elements 面板里看到的就是这棵树。
事件:点击和输入是怎么被接住的
事件(event)指"用户或浏览器做了点什么":点击(click)、输入(input)、提交表单(submit)、页面加载完(load)、键盘按下(keydown)。事件监听(event listener)是"盯住某个元素的某类事件,发生时就执行我给的这段代码";被交出去执行的那段代码叫回调(callback)。
// 原生写法:找元素 → 挂监听 → 给一个回调
document.getElementById("btn").addEventListener("click", () => {
console.log("被点了"); // 在浏览器 Console 里打印一行,最常用的排查手段
});
// React 写法:把"发生什么"直接写在标签上
<button onClick={() => setN(n + 1)}>点我</button>
两种写法都在做同一件事,区别只在于"谁来负责把事件接到那棵树上"。React 里那个 onClick 不是你手动挂的监听,而是 React 在渲染时替你接上的——这也解释了 §08 里的水合:水合的一部分工作,就是把这些监听真正挂到浏览器的 DOM 上。
命令式 vs 声明式:现代前端写法的分水岭
这是本节最值钱的一对词,理解它之后,你能看懂 AI 为什么那么写代码:
| 命令式(imperative) | 声明式(declarative) | |
|---|---|---|
| 你写的代码在说 | "怎么一步步做":找到按钮、挂监听、改文字 | "应该是什么样":如果 n 是 3,就显示 3 |
| 谁去改页面 | 你自己,一行一行手动改 | 框架,它算出差别后替你改 |
| 典型代码 | label.textContent = "..." | <h1>点了 {n} 次</h1> |
| 出错的方式 | 漏改一处 → 界面和数据不一致 | 状态设计混乱 → 重新渲染太频繁或死循环 |
| 你更常听到的说法 | "直接操作 DOM" | "数据驱动视图"、"状态是唯一真相来源" |
React 的核心卖点就是声明式:你只管把状态算对,页面长什么样由状态推导出来。代价是你得放弃"我直接改那一行"的直觉——新手最常见的错误之一,就是在 React 里用 document.getElementById 硬改页面,结果下一次重新渲染时改动被覆盖掉。这就是"别在 React 里直接操作 DOM"这句话的由来。
虚拟 DOM 是什么,以及它"不是"什么
虚拟 DOM(Virtual DOM)是一层用 JavaScript 对象描述的"假树",它对应着真实 DOM 应该长什么样。重新渲染时,React 先生成一份新的假树,和上一份对比,算出"真正变了的是这几处",然后只改那几处真树。
关于它最常见的误解是"虚拟 DOM 比手写快"。准确的说法是:它让框架能自动做够好的更新,而你不用手写更新逻辑。在极端场景下,一个精心手写的更新可以比它更快;虚拟 DOM 买到的是"开发省心 + 性能不差",不是"性能最强"。当 AI 说"虚拟 DOM 让它更快"时,你可以放心地把它理解为一句简化的说法,而不是精确结论。
你什么时候才会真的碰到 DOM
- 看到
useRef:想让某个输入框自动获得焦点、想拿到元素的实际宽高、想接入一个不认识 React 的第三方库(图表、地图、编辑器)。 - 看到
document.querySelector/window(window是浏览器给页面的"总控制台"对象,读写当前窗口、地址、本地存储都从它出发):在纯 JavaScript 项目里,或者在框架外挂的一段脚本里。React 组件里出现它们,通常是"绕过了框架"的信号,值得问 AI 一句为什么。 - 看到"SSR 里不能直接用
window":因为服务端根本没有浏览器、没有 DOM,所以那段代码在服务端会直接报错window is not defined。这是服务端 / 客户端分界最典型的一道报错,§08-7 会接着讲。
08CSR / SSR / SSG / ISR / 水合:AI 最容易唬人的一节
为什么会有这么多"渲染"
渲染(render)在前端有两个常用意思,先分清:① "把数据变成界面描述"(§06 说的组件重新渲染);② "把界面变成浏览器能显示的 HTML"。这一节说的是第二种,问题是:这份 HTML 到底在什么时候、在哪台机器上生成?
之所以有这么多方案,是因为有三个需求天然互相拉扯:
- 首屏要快:访客打开网页的一瞬间就想看到东西,不想对着白屏等。
- 要能交互:看到之后还得能点、能输入。
- 内容要新:数据变了,页面得跟着变。
把这三点排不同优先级,就长出了 CSR / SSR / SSG / ISR 这四种模式。它们不是"谁更先进"的排名,而是四种取舍。下面每一种都按同一个模板讲:HTML 在哪生成、JS 什么时候到、最适合什么、代价是什么。
CSR:先给一个空壳,页面在浏览器里现拼
CSR(Client-Side Rendering,客户端渲染)的做法是:服务器只发一个几乎空的 HTML 和一大包 JavaScript,浏览器下载、执行 JS 之后,才把界面拼出来。你访问的其实是一个 SPA(Single Page Application,单页应用)——整个网站只有一个 HTML 页面,切换页面时不再向服务器要新页面,而是 JS 自己换内容。
- 最适合:后台管理界面、需要大量本地交互的应用(在线编辑器、聊天工具)。
- 代价:首屏慢(要等 JS 下载执行完才看得到东西);搜索引擎爬虫可能抓不到内容,对 SEO(Search Engine Optimization,让搜索引擎更好收录你)不友好。
- 你会看到的现象:打开页面先白一下,然后内容"刷"地出现;F12 看网页源码,正文是空的。
SSR:服务器先拼好 HTML 再发过来
SSR(Server-Side Rendering,服务端渲染)的做法是:每次有人访问,服务器当场把 HTML 拼好发过去。访客立刻能看到完整内容(首屏快、SEO 友好),但页面刚到时还不能交互——因为负责交互的 JavaScript 还在路上,或者还没执行。
- 最适合:内容对搜索引擎重要、且每个人看到的内容可能不同的页面(电商商品页、社交动态)。
- 代价:服务器每次请求都要干活,人多时压力大;比静态页面慢一点。
SSG:提前拼好,来谁都发同一份
SSG(Static Site Generation,静态站点生成)的做法是:在构建那一刻(你跑 npm run build 的时候,见 §05)就把所有页面的 HTML 生成好,之后谁访问都发同一份现成的文件。它最快、最省钱、最安全(没有服务器端代码在跑)。
- 最适合:内容不常变的页面:博客、文档、产品介绍、官网。
- 代价:内容变了必须重新构建、重新部署;不适合"每次都要看最新数据"的页面。
ISR:静态页 + 过期后偷偷重做一份
ISR(Incremental Static Regeneration,增量静态再生)是 Next.js 提出的一种折中:页面仍是静态生成的、发得飞快,但你给它定一个"保质期";过期后第一个访问的人会看到旧页面,同时服务器在后台悄悄生成新的一份,后面的人就拿到新的了。
- 最适合:内容会更新但不要求秒级实时的页面:商品列表、排行榜、新闻列表。
- 代价:有过期窗口,不是"永远最新"。
- 注意版本差异:Next.js 16 把缓存写法改成了
use cache指令,官网把旧的fetch缓存配置明确标为 "Previous Model"(旧模型)。概念没变,写法变了——所以看到老教程里的写法时,先让 AI 按当前版本文档核对一遍。
水合(hydration):为什么"页面看着有了,却点不动"
这是本节最该记住的一个词。Next.js 官方文档给的定义是:水合是 React 把事件处理器接到 DOM 上、让静态 HTML 变得可交互的过程。用一个类比:
服务器发过来的 HTML 像一张印好的纸质菜单:菜名、价格都在,看得清清楚楚,但你指着它点菜它不会理你。水合就是"把这张纸变成真正的服务员":样子一点没变,但从这一刻起你点它就真的有反应了。
这个类比在哪里不准:现实中当服务员来"接管"这张纸时,他必须让纸上的内容和他脑子里记的完全一致;如果客人已经照着纸改了字(比如输入框里打了字),或者纸上写的和他记的不一样,他就会报错——这就是下面的 hydration mismatch。
hydration mismatch(水合不匹配)是最常见的 SSR 类报错,意思是:服务器渲染出的 HTML,和浏览器第一次渲染算出来的结果对不上。三种高发原因:
| 原因 | 现场长什么样 | 怎么改 |
|---|---|---|
| 代码用了"每次结果都不同"的东西 | 页面里显示时间、随机数、Math.random()、Date.now() | 把它挪到"浏览器里才执行"的地方(React 里是 useEffect 里再赋值) |
| 服务端和浏览器的环境不同 | 读 window、localStorage、判断屏幕宽度后渲染不同内容 | 服务端不读这些;改成先渲染一个占位,等到了浏览器再补 |
| HTML 结构嵌套不合法 | <p> 里套了 <div>、表格里少了 <tr>,浏览器会自动"修正"你的 HTML,于是和服务器算的不一样 | 检查那一块的标签嵌套 |
还有一类现象值得单独记住:页面显示了、但按钮点不动。这通常是"水合还没完成或失败了"——HTML 到了,JS 还没执行完或报错了。所以看到"样式正常但没反应",第一反应应该是去看浏览器 Console(F12 里的报错窗口,见 §07-1)里有没有红色的 hydration 报错,而不是去改那个按钮的代码。
Server Components 与 "use client":这一代的分界
在 Next.js 这一代(App Router)里,组件被分成两类,AI 会频繁提到这个分界:
- 服务端组件(Server Component):默认就是它。在服务器上执行,可以直接读数据库、读文件、拿密钥,而不会把这些代码发到访客的浏览器里。它不能使用
useState、onClick这类依赖浏览器的能力。 - 客户端组件(Client Component):文件顶部写了
"use client"的组件。它在浏览器里跑,能用 state、事件、window。代价是它的代码会进到访客浏览器里,所以不能在里面写任何密钥。
所以看到报错 useState only works in Client Components. Add the "use client" directive,它的意思不是"你代码写错了",而是"你想用的能力需要在浏览器里跑,请把这一小块标成客户端组件"。这也是当前 AI 最容易给出过时建议的领域之一:如果你照着一个旧教程把整个页面都标成 "use client",就把服务端渲染的好处丢掉了。合理的心智模型是:默认留在服务端,只在真正需要交互的那一小块标客户端。
六个词放一起(这张表值得存下来)
| 词 | HTML 在哪生成 | 什么时候生成 | 适合什么 | 别当什么用 |
|---|---|---|---|---|
| CSR | 浏览器 | 每次访问、下载 JS 之后 | 后台界面、重交互应用 | 别指望它首屏快、被搜索收录 |
| SSR | 服务器 | 每次有请求时 | 内容要收录、每个人看到的不同 | 别以为它"到了就能点"(还要水合) |
| SSG | 构建时 | 跑 build 那一刻 | 博客、文档、官网 | 别用在每分钟都变的页面上 |
| ISR | 构建时 + 后台 | 构建时先出一份,过期后后台重做 | 商品列表、排行榜 | 别当成"永远实时" |
| 水合 | —(不是生成方式) | HTML 到达后、JS 执行时 | — | 别当成"再渲染一次页面" |
| Server Component | 服务器 | 按上面的模式决定 | 取数据、读密钥、放心写业务逻辑 | 别在里面用 state / 事件监听 |
看不懂这张表也没关系,把它当索引:下次 AI 说"我把它改成 SSR 了",你至少能反问一句"那它还需要水合吗?首屏能直接交互吗?"——这一句就已经比大多数人专业了。
09CSS 那些词:类名、Tailwind、flex、grid、响应式
CSS 管什么
CSS 只回答一类问题:哪个元素,长什么样。它的写法由两部分组成:选择器(selector,选中谁)和声明块(花括号里写"哪一项属性等于什么值")。
/* 选择器 button → 声明块 { 属性: 值; } */
button {
background: #6366f1;
border-radius: 8px;
}
.card { padding: 16px; } /* .card = 选中所有 class="card" 的元素 */
#title { font-size: 24px; } /* #title = 选中 id="title" 的那一个元素 */
类名(class)是 HTML 元素上的一个属性,用来给元素贴标签,好让 CSS 选中它。元素上可以贴很多个标签:class="card featured"。
为什么 AI 老在 className 里塞一长串
当你看到 <div className="flex items-center gap-2 rounded-lg bg-slate-800 p-4">,那不是乱码,而是 Tailwind CSS 的写法。它的思路叫"工具类(utility class)":每一个类名只对应一条样式声明,你像贴标签一样把需要的样式一个个贴上去。上面那串翻译过来就是:
| 类名 | 它在说什么 |
|---|---|
flex | 让子元素排成一行(用 flex 布局) |
items-center | 子元素在另一条轴上居中 |
gap-2 | 子元素之间留一点空隙(2 是它的一套刻度,不是像素) |
rounded-lg | 圆角大一点 |
bg-slate-800 | 背景用 slate 色板的第 800 档(深灰蓝) |
p-4 | 内边距一圈 |
为什么 AI 爱用它?因为它让"样式"和"结构"写在同一个地方,AI 不用来回跳文件,也不容易起错类名。代价是标签会变长,而且你在编辑器里看不出最终长什么样——需要靠经验把 gap-2 读成"间距"。你就把它当成一份密集的缩写表,不必记住具体刻度。
Tailwind 4 的安装方式和 3 完全不同(4 版不再需要那套复杂的配置,也不再需要 @tailwind 三行指令)。用 Vite 时,现在是两步:
# 1. 装包
npm install tailwindcss @tailwindcss/vite
# 2. 在 vite.config.ts 里加插件
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})
# 3. 在你的 CSS 入口文件第一行写这一句,就完事
@import "tailwindcss";
这个例子值得记住,因为它演示了一类高频现象:AI 按三年前的记忆给你 Tailwind 安装步骤,你照着做会失败。遇到"配置类步骤"时,最省时间的做法是让 AI 先去读当前版本文档再动手(§11 有可直接复制的追问模板)。
flex 与 grid:两条轴线 vs 一张格子
这两个是布局的主力,也是 AI 嘴里出现频率最高的 CSS 词。MDN 讲 flexbox 时开门见山:用它要想的是两条轴。
.row {
display: flex;
gap: 12px; /* 子元素间距 */
align-items: center; /* 交叉轴居中 */
justify-content: space-between; /* 主轴两端对齐 */
flex-wrap: wrap; /* 装不下就换行 */
}
.gallery {
display: grid;
grid-template-columns: repeat(3, 1fr); /* 三等分:1fr = 占一份宽 */
gap: 16px;
}
@media (max-width: 640px) {
.gallery { grid-template-columns: 1fr; } /* 小屏一列 */
}
一句话分界:要"排一排"用 flex,要"划格子"用 grid。看到 display: flex 不用慌,它八成在处理你正盯着的那个间距或对齐问题。
响应式与断点:一份代码适配手机和电脑
响应式设计(responsive design)指让同一份页面在不同屏幕上都好用。MDN 在教学大纲里把它定义得很直白:"设计出能灵活适应不同设备屏幕尺寸、分辨率的网页布局"。实现手段主要是媒体查询(media query)——一句"当屏幕宽度小于 X 时,用另一套样式"。那个 X 就叫断点(breakpoint)。
/* 原生 CSS 写法 */
@media (max-width: 640px) {
.sidebar { display: none; } /* 手机上直接收掉侧边栏 */
}
/* Tailwind 的写法:类名前面加前缀,sm/md/lg 都是预设断点 */
<div className="grid grid-cols-1 md:grid-cols-3"> ... </div>
/* 读法:手机上一列;medium(中屏)以上三列 */
所以你看到 md: lg: sm: 这些前缀,意思就是"从某个屏幕宽度起才生效"。这就是本文这个页面为什么在手机上侧边栏会消失——同一条规则。
深色模式、CSS 变量、主题
CSS 变量(也叫自定义属性)是"给一个值取名字",好处是改一处、全站变:
:root {
--brand: #6366f1; /* 定一个名字 */
}
button { background: var(--brand); } /* 用它 */
@media (prefers-color-scheme: dark) { /* 系统开了深色模式时 */
:root { --page-bg: #0f1117; }
}
prefers-color-scheme 是浏览器告诉你"用户系统偏好深色还是浅色"的方式,深色模式就靠它(本文这份教程同理:整站用一组变量,所以所有页面颜色一致)。当你看到 AI 说"用 CSS 变量统一主题",说的就是上面这件事。
"样式不生效"的排查顺序
这是前端最高频的一类问题,而且有一条固定的排查顺序,照着走能省下大量瞎改:
CSS 有没有被引入?Tailwind 项目里,你写的新文件在不在它扫描的目录里?改配置之后 dev server 有没有重启过?
Tailwind 的类名拼错不会报错,只是没效果——这是它最坑的地方。items-center 写成 item-center,你只会看到"没居中"。
Elements 面板里选中那个元素,右下角 Styles 里被划横线的规则表示被别的规则覆盖了。这叫优先级(specificity):越具体的选择器赢;!important 是强行插队的写法,能不用就别用。
改了半天没变,硬刷新一次(Mac Cmd+Shift+R / Windows Ctrl+F5)。但这一步要放在最后——一开始就怪缓存,会让你错过真正的错因。
10从 localhost 到别人能看到:部署、环境变量、CORS
localhost:3000 是什么,为什么别人打不开
localhost 是"这台机器自己"的固定名字(技术上对应 127.0.0.1,叫回环地址),3000 是端口——一台机器上可以有很多程序同时在提供网页服务,端口就是区分它们的门牌号。所以:
http://localhost:5173= "我自己这台机器 5173 号门口的那个程序",那个程序就是你刚敲的npm run dev。- 它只对你有效。你把它发给朋友,朋友浏览器里的
localhost指的是朋友自己那台机器,那里没有你的程序,所以打不开。这是新手最常见的"我以为我做好了"。
打开浏览器地址栏看一眼:如果它写的是 localhost 或 127.0.0.1,那么无论它在你屏幕上多完美,别人都看不到。你需要的是 §10-2 的部署(得到一个真正能被访问的网址)。
让世界看到:两条路
| 静态托管 | 平台 / 服务器 | |
|---|---|---|
| 放上去的是什么 | 就是 npm run build 出来的 dist/ 文件夹 | 整个项目,平台替你构建和运行 |
| 能跑什么 | 只有前端:HTML / CSS / JS | 还能跑服务端代码、定时任务、数据库 |
| 常见选择 | GitHub Pages、Cloudflare Pages、Netlify、Vercel(静态模式) | Vercel / Netlify(默认)、自己的云服务器 |
| 适合 | 文档、博客、演示页 | 登录、支付、需要密钥的接口 |
最省事的一条路(也是 AI 最常推荐的):把代码推到 GitHub,然后用 Vercel / Netlify 连上这个仓库,之后每次 push 它自动帮你构建并发布。你甚至不需要在本机跑构建。手工版的完整流程是:
npm run build # 1. 生成产物(dist/ 或 .next/)
npm run preview # 2. 本地按"真实上线"的方式跑一遍(Next.js 项目用 npm run start)
# 3. 把产物上传给托管平台(网页上拖拽、或平台读你的 git 仓库自动做)
这三步的顺序值得记住:先 build、再 preview、最后才部署。跳过第 1 步直接部署,就是把"没翻译过的源码"交给浏览器。
域名、DNS、HTTPS 三个词
- 域名(domain):
example.com这种给人看的地址。机器之间实际靠一串数字 IP 通信,域名是它的外号。 - DNS:把域名翻译成 IP 的"电话簿"。你说"我要访问 example.com",DNS 告诉你该去哪台机器。所以"域名解析没生效"=这本电话簿还没更新,通常等几分钟。
- HTTPS:浏览器和服务器之间加密传输(网址前的锁图标)。现在托管平台基本都免费自动配好。如果浏览器把网站标成"不安全",往往不是网站坏了,而是证书没生效或过期。
环境变量与 .env:为什么密钥不能写进前端
环境变量(environment variable)是"不写死在代码里、运行时再给程序的值",最常用来放密钥、数据库地址、不同环境的配置。它们通常写在一个叫 .env 的文件里:
# .env 文件(这个文件绝对不能提交进 git)
DATABASE_URL=postgres://user:pass@host/db
PAYMENT_SECRET=sk_live_xxxxxxxx
# Next.js:只有 NEXT_PUBLIC_ 开头的才会被发到浏览器
NEXT_PUBLIC_SITE_NAME=我的网站
# Vite:只有 VITE_ 开头的才会被发到浏览器
VITE_API_BASE=https://api.example.com
凡是会进入浏览器的东西,都是公开的。前端代码、前端能读到的环境变量、请求里带的参数,任何访客按 F12 都能看到。NEXT_PUBLIC_ / VITE_ 这类前缀的含义不是"加密",而是"会被公开地打包进去"。所以:支付密钥、数据库密码、AI 服务的 API Key,一律只能放在服务端。如果你发现 AI 把它们写进了前端文件或前端环境变量里,立刻停下来改,并且去把那个密钥作废重发一个——密钥一旦发到过浏览器,就等于已经泄露了。
配套的两个动作:.env 要写进 .gitignore(这样 git 不会提交它);同时提供一份 .env.example(只有键名、没有真值)告诉别人需要配哪些项。看到 AI 生成 .env.example,那是好习惯,不是多此一举。
CORS / 跨域:报错长什么样、责任在谁
你在浏览器控制台里八成见过这句红字:
Access to fetch at 'https://api.example.com/data' from origin 'http://localhost:5173'
has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present
on the requested resource.
它说的是什么?MDN 的解释可以拆成三句人话:
- 网页里发请求(
fetch是浏览器自带的"要数据"函数,XMLHttpRequest是它的老前辈)时,浏览器默认遵守同源策略(same-origin policy):只能向"同一个来源"要数据。来源由三样组成——协议 + 域名 + 端口,三者有一样不同就算"跨域"。localhost:5173和localhost:3000端口不同,也算跨域。 - CORS(Cross-Origin Resource Sharing,跨源资源共享)是打破这道墙的正规方式:需要服务器在响应头(服务器回给你的那份"附加说明",和正文一起发回来)里明确说"我允许这个来源来拿"(
Access-Control-Allow-Origin)。有些请求还会先发一个试探性的预检请求(preflight),得到允许才发真请求。 - 所以:这个错是服务器没放行,不是你前端代码写错了。
不要对 AI 说"帮我修这个跨域错误"——那会让它在前端乱试。说:"这个接口没有返回 CORS 允许头。请告诉我该在服务端加什么响应头,或者有没有必要走服务端代理转发。"把球踢回正确的一侧,通常五分钟就解决;让 AI 在前端硬改,可能耗一下午。
CDN 与缓存:为什么"我改了,线上还是旧的"
MDN 对 CDN(Content Delivery Network,内容分发网络)的说明是:它是分布在很多地点的一组服务器,这些服务器存放数据的副本,从而让离用户最近的那台来响应请求。用人话讲:把你的静态文件复印很多份放到各地,谁离哪近就从哪拿。
CDN 靠缓存(cache,把文件先存一份,下次直接给)来提速,而缓存正是"改了代码线上没变"的头号嫌疑人。按这个顺序排查:
本地跑过 npm run build 了吗?部署平台的那次构建成功了吗?很多"缓存问题"其实是那一次构建失败了,或者压根没触发。
硬刷新(Cmd+Shift+R / Ctrl+F5)、或无痕窗口打开。如果无痕窗口是新的,那就是你本地缓存。
托管平台的"清除缓存 / 重新部署",或者等它自己的缓存过期。这一步涉及的就是 §08-4 那个 ISR 的"保质期"概念——静态页面本来就是"过期后才更新"的。
11遇到不认识的词,三步自救(含能直接粘给 AI 的话术)
第一步:先归类,再看细节
这一整篇其实都在训练同一个动作:听到一个生词,先判断它属于哪一层。定层之后,你就知道它大概在管什么、该不该紧张。下面这张表可以直接拿来用——按词里出现的"线索"判断:
| 词里出现这些线索 | 它属于哪一层 | 你该默认怎么对待它 |
|---|---|---|
html、div、标签、元素、结构、标签嵌套 | 内容层 | 关心"内容对不对、结构合不合法",一般不影响功能 |
css、class、style、flex、grid、tailwind、responsive、dark | 样式层 | 关心"是不是你要的样子";不生效一般不影响逻辑 |
component、props、state、hook、render、useXxx、jsx | 交互层(框架) | 这是功能的地盘,值得问清楚 |
node、npm、install、build、bundle、vite、typescript、tsconfig(TypeScript 的配置文件) | 工具链层 | 多半是"环境问题",照命令敲即可,别改业务代码 |
deploy、vercel、env、cors、cdn、cache、domain、ssl | 运行与交付层 | 关心"别人能不能访问、安不安全" |
ssr、ssg、csr、isr、hydration、server component | 横跨交互与交付 | 最容易被唬住的一类;§08 那张六词表可以对照 |
"这个词是让页面'长什么样'变,还是让页面'能不能用'变,还是让'别人能不能访问'变?"三个选项覆盖了九成情况。前两类你都可以从容地慢慢学,第三类(能不能访问、会不会泄露密钥)才需要立刻处理。
第二步:把问题丢回去(五条可直接复制的追问模板)
新手最大的浪费不是不懂,而是用错了提问方式。下面是五条经过验证的模板,直接复制去用:
# 模板 1:只要解释,不要代码(防止它一口气甩出 200 行)
用日常语言解释「XXX」是什么、它解决什么问题,最多三句话,先不要给我代码。
# 模板 2:让报错自己交代(把报错原文整段贴进去)
这是我看到的完整报错。请先指出里面哪几个词是术语并逐个人话解释,
再说清这是环境问题还是代码问题,最后只给我需要敲的那一条命令:
<粘贴报错原文>
# 模板 3:先讲计划再动手(防止它顺手改坏别的地方)
动手之前先告诉我:你打算改哪几个文件、每个文件改什么、为什么必须改它。
我同意之后你再动手,不要顺手重构其他部分。
# 模板 4:要最小例子,不要完整项目
给我一个能跑的最小例子(一个文件或最多两个文件),只演示「XXX」这一件事,
不要脚手架(一键生成整套项目骨架的模板)、不要额外依赖,并告诉我怎么验证它跑对了。
# 模板 5:让代码自己解释自己(读别人代码时最有用的条)
把这段代码里的行话用中文注释标出来:每一步在干什么、哪些是框架要求、
哪些是你这个产品自己的规则(业务逻辑)。先不要改功能。
还有一条专治"AI 按老版本记忆办事"的说法:
这个配置是哪个版本的做法?请先去读当前版本的官方文档确认,再给我步骤。
如果文档里的写法和你的记忆不一致,以文档为准,并告诉我差在哪。
第三步:分清"必须懂"和"可以不理"
你不可能也不需要把前端全学一遍。按"不懂会付出什么代价"分三档:
| 档位 | 词 | 为什么是这一档 |
|---|---|---|
| 必须懂(不懂会出事) | 前端 / 后端(代码跑在哪)、环境变量与密钥泄露、build 与 dev 的区别、localhost 不能给别人、CORS 是服务端的事、依赖与 lock 文件 | 这些直接决定"会不会泄露密钥""能不能上线""报错时该往哪边修" |
| 值得懂(懂了你判断力明显变强) | 组件 / props / state / hook、DOM、事件、CSR / SSR / SSG / ISR、水合、组件与框架的区别、flex / grid、响应式断点 | 这些是 AI 说话的语法;懂了你才能判断它有没有在胡说、方案合不合理 |
| 可以先不理(用到再学) | 虚拟 DOM 的实现细节、tree-shaking 与 source map 的原理、Turbopack / Rolldown / webpack 的区别、semver 规则细节、优先级计算规则、无障碍与 SEO 细节、具体某个库的 API | 不知道它们也完全不影响你把项目跑起来;它们属于"优化与深水区" |
一次真实对话长什么样
你:我页面报错了,怎么修?
AI:(猜一个原因,改三处代码,又引入两个新错误;你更慌了,于是再问一遍)
为什么这样问会翻车:没给报错原文、没说清在哪一步发生、没限定它"只诊断不改代码"。AI 只能靠猜,而它在猜的时候会顺手重构你的文件(重构=在不改功能的前提下重写代码结构)——问题没解决,你的代码还变陌生了。
你:我在 npm run dev 之后按按钮没反应。浏览器 Console 里的报错是:<粘贴原文>。先别改代码,请告诉我:① 这个报错属于哪一类;② 里面的术语各是什么意思;③ 我该先验证哪一件事。
AI:(说清是水合失败导致监听没挂上 → 定位到某个组件用了 window)
为什么有效:给了原文(可核对)、限制了动作(只诊断)、要了顺序(先验证哪件事)。这三样加起来,AI 就从"猜谜机器"变成"排查助手"。
1217 组最容易混的名词:一张对照表
下面每一组都是"长得很像、说错会误事"的搭配。第三列就是你该记住的那一句分界。
| 长得像的两个词 | 各自是什么 | 一句话分界 / 混了会怎样 |
|---|---|---|
| 前端 vs 后端 | 浏览器里跑的那一半 vs 服务器上跑的那一半 | 看"这段代码跑在谁的电脑上"。混淆的后果:把密钥写进前端(严重) |
| 库 vs 框架 | 你调用它(React)vs 它调用你(Next.js) | 换个框架,项目结构、文件位置、渲染方式全变;换个库,往往只是换零件 |
| React vs Next.js | 库 vs 框架 | Next.js 用 React 当零件。教程代码不一样,多半是框架不同 |
| npm vs npx | 装到项目里 vs 临时借来跑一次 | 建新项目用 npx;给项目装依赖用 npm |
| npm vs pnpm / yarn | 三种包管理器,同一份 package.json | 看项目根目录里已有哪个 lock 文件就用哪个,别混用 |
package.json vs package-lock.json | 我要哪些包(可以写范围)vs 实际装了哪个确切版本 | 前者人写、后者自动生成;两个都要提交进 git |
| dependencies vs devDependencies | 运行时要用的 vs 只在开发和构建时用的 | 上线打包时只看前者(多数情况下);npm install -D 装的是后者 |
| 包 vs 依赖 | 那个东西本身 vs "我用了它"这层关系 | 同一个包,在你项目里是你的依赖;在别人项目里可能不是 |
| props vs state | 外面递进来的(只读)vs 自己记着的(用更新函数改) | "该用 state 的地方写成普通变量"=改了界面不变,新手第一大坑 |
| 组件 vs 普通函数 | 返回界面、首字母大写的才是组件 | 名字不大写,React 会把它当成普通 HTML 标签,渲染不出来 |
| DOM vs 虚拟 DOM | 浏览器里那棵真实的树 vs 框架在内存里比对的假树 | 你操作的是 DOM;框架为了少改 DOM,才用了虚拟 DOM |
| CSR vs SSR | 浏览器里现拼 vs 服务器当场拼好 | 一个首屏慢、一个服务器压力大;都能交互,但都要经过水合 |
| SSG vs ISR | 构建时生成一次 vs 生成后还能过期重做 | 内容会变但不需要秒级实时 → ISR;基本不变 → SSG |
| dev vs build | 给你自己看的 vs 给全世界看的 | dev 能跑不等于能上线;上线前必须 npm run build 过一遍 |
| 环境变量 vs 密钥 | 一个存储机制 vs 一种不能公开的值 | 环境变量不是保险箱:带 NEXT_PUBLIC_ / VITE_ 前缀的会进浏览器 |
| 域名 vs localhost | 别人能访问的地址 vs 只有你自己能访问的地址 | 地址栏里有 localhost,就别指望别人打开 |
| 跨域报错 vs 接口坏了 | 浏览器拦下了请求 vs 服务器真的出错了 | 跨域要在服务端加允许头;接口坏了要看服务端日志。修错一边=白干 |
13名词速查表:AI 说出这个词时,它在说哪一层
按层排成四张表。第三列写的是"什么时候你必须懂、别当什么用"——它比定义更值钱,因为大多数词你其实只需要知道"什么时候该管它"。
内容与样式层
| 术语 | 一句话它是什么 | 为什么重要 / 什么时候你必须懂 · 别当什么用 |
|---|---|---|
| HTML | 描述页面内容和结构的语言 | SEO 和可访问性都建立在它上面。别把它当成"过时的东西"——所有框架最终都生成它 |
| 标签 / 元素 / 属性 | 尖括号标记 / 标记加内容 / 标记里的补充信息 | 报"HTML 嵌套不合法"时你要能看懂这类说法 |
| DOM | 浏览器把 HTML 变成的内存里的树 | 所有"页面上的变化"最终都是改它。别在 React 里直接改它(会被覆盖) |
| CSS | 描述"哪个元素长什么样"的语言 | 改样式不算功能改动,但影响可用性(手机上看不清就是 bug) |
| 选择器 / 类名 | 选中谁 / 元素身上的标签 | 调样式时最先看的东西。别靠 !important 硬压 |
| 优先级(specificity) | 多条规则冲突时谁赢 | "样式不生效"的第三顺位原因。别一上来就怪缓存 |
| CSS 变量 | 给一个值取名字,改一处全站变 | 做主题 / 深色模式时是标配 |
| Tailwind | 把样式拆成一个个小类名的写法 | 看懂 flex items-center gap-2 是读现代前端代码的基本功。别背刻度 |
| flex / grid | 排成一排 / 划成格子 | 布局问题九成出在这两个上。别用绝对定位硬凑间距 |
| 响应式 / 断点 / 媒体查询 | 一份代码适配所有屏幕 / 切换的宽度值 / 实现它的规则 | 用户用手机打开你的页面时你就必须懂它 |
交互层
| 术语 | 一句话它是什么 | 为什么重要 / 什么时候你必须懂 · 别当什么用 |
|---|---|---|
| JavaScript(JS) | 让页面能动的语言 | 一切交互的地基。框架只是它的写法 |
| TypeScript(TS) | 给 JS 加了类型检查的版本 | AI 生成的项目大多是 TS;类型报错是"提前发现错误",不是"代码坏了" |
| 组件(component) | 返回界面长相的函数 | 现代前端的基本单位。别把"组件"和"页面"混为一谈(页面通常由多个组件拼成) |
| JSX / TSX | 长得像 HTML、写在 JS 里的写法 | 必须被构建翻译;className、闭合标签是它的规矩 |
| props | 父组件传给子组件的只读数据 | 子组件不能改 props,改了会报错——这是"数据从哪来"的规则 |
| state | 组件自己记着的、会变的值 | 改了会触发重新渲染。别把它写成普通变量(界面不会更新) |
| hook | 以 use 开头、给组件接能力的函数 | 看到 useState / useEffect 要知道它在给组件加什么。两条铁律:只在顶层调用、只在组件里调用 |
| 重新渲染(re-render) | 重新执行组件函数,算出新的界面描述 | 不是刷新页面。性能问题常从"谁在频繁重新渲染"入手 |
| 虚拟 DOM | 内存里的假界面树,用来算最小改动 | 别以为它"一定更快"——它买的是开发省心和够好的性能 |
| 事件 / 监听 / 回调 | 发生了什么 / 盯住它 / 发生时执行的那段代码 | "点了没反应"的问题都要从这里查 |
| 声明式 vs 命令式 | 描述"该是什么样" vs 一步步下命令 | 理解它你才明白为什么不能在 React 里手动改 DOM |
工具链层
| 术语 | 一句话它是什么 | 为什么重要 / 什么时候你必须懂 · 别当什么用 |
|---|---|---|
| Node.js | 能在电脑上跑 JavaScript 的运行环境 | 所有前端工具的地基。版本太低是大量怪报错的根因 |
| npm / pnpm / yarn / bun | 包管理器:按清单把别人的代码装下来 | 拿到项目先看 lock 文件决定用哪个。别混用 |
| npx | 不安装、临时跑一次某个工具 | 建新项目、跑一次性命令时用它 |
包 / 依赖 / package.json / lock 文件 / node_modules | 别人的代码 / 你用了它 / 清单 / 版本锁定 / 实际下载的地方 | 一类报错(找不到模块)全靠这组词定位。node_modules 不进 git,但也不能随手删 |
| 构建(build)/ 打包(bundle) | 把源码翻译、合并成浏览器能跑的文件 | 没有它,TS / JSX / 模块都用不了;部署的是产物不是源码 |
| dev server | 本地开发用的临时服务器 | 它只对你有效;那个终端窗口关了网页就没了 |
| HMR | 改代码后页面局部自动更新 | 没有它你每次都要手动刷新——所以别关那个终端 |
| minify / tree-shaking / source map | 压缩 / 删掉没用到的代码 / 错误位置的对照表 | 看懂报错栈时要靠 source map。别拿压缩后的代码改 bug |
| Vite / webpack / Turbopack / Rolldown | 同一层的构建工具,不同实现 | 知道它们是"那个层的工具"就够了。别纠结哪个更快 |
| 端口(port) | 一台机器上的门牌号 | :5173 和 :3000 是两个不同的服务;端口不同就算跨域 |
运行与交付层
| 术语 | 一句话它是什么 | 为什么重要 / 什么时候你必须懂 · 别当什么用 |
|---|---|---|
| 部署(deploy) | 把产物放到别人能访问的机器上 | "能不能上线"就看这一步。别把 dev 地址当成品 |
| localhost | "这台机器自己"的固定名字 | 地址栏里出现它,就等于"别人看不到" |
环境变量 / .env | 不写死在代码里的配置值 | 密钥安全的唯一正解在服务端;带公开前缀的变量会进浏览器 |
| 域名 / DNS / HTTPS | 给人看的地址 / 域名到机器的电话簿 / 加密传输 | 绑定域名、排查"打不开""不安全"时要用 |
| CORS / 跨域 / 同源策略 | 浏览器默认不许向别的来源要数据 / 这件事的名字 / 那条默认规则 | 遇到这类报错,改服务端而不是前端。这是最能省时间的知识点之一 |
| CDN / 缓存 | 把文件复印到各地就近发给用户 / 存一份下次直接用 | "改了线上没变"的头号嫌疑人(但要排到最后一步才动手清) |
| CSR / SSR / SSG / ISR | 四种"HTML 在哪、什么时候生成"的方案 | 不是好坏排名而是取舍。听到这些词时你能反问"首屏能否交互" |
| 水合(hydration) | 把静态 HTML 变可交互的那一步 | "页面显示了但点不动"几乎都是它。报错关键词是 hydration mismatch |
Server Component / "use client" | 在服务器上跑的组件 / 把某个组件标成在浏览器跑的指令 | Next.js 这一代的必答题;密钥只能待在服务端那一侧 |
| SEO | 让搜索引擎更容易收录你的页面 | 内容站、官网才需要重点关心;后台系统可以暂时不管 |
14六个常见误解(以及它们为什么那么诱人)
"use client")、配置问题(CORS、路径别名)、真正的逻辑 bug。四类的修法完全不同,而前三类改业务代码是纯浪费——§04-6 那张表就是为前三类准备的。localhost 意味着这个"服务器"就是你自己。真正的验收标准是:用别人的手机、别人的网络打开一个真实网址,点一遍主要功能。这也顺带解释了为什么很多问题只在部署后才冒出来(路由配置、环境变量、缓存策略在本地都被绕过了)。npm run build → 部署 → 再刷新看。15参考来源与证据边界
文中的版本数字是怎么来的(你可以照着复核)
本文所有版本号不是凭记忆写的,是写作当天(2026-09-13)从官方源读出来的。复核方式:
curl -s https://nodejs.org/dist/index.json | head -40 # 找 "lts" 字段那一条
# 其余版本号的复核命令在 §00「本文校对的版本快照」上方,这里不重复
读数结果就是 §00 那张快照表(npm view 直接读 npm 官方接口)。另外两处硬约束也是当天从官方文档里核出来的:Next.js 要求 Node 最低 20.9、Vite 要求 Node 20.19+ 或 22.12+——它们决定了"别人的项目为什么在我机器上跑不起来"。
主要来源
| 来源 | 本文用它核实了什么 | 链接 |
|---|---|---|
| MDN Web Docs(DOM 介绍) | DOM 是"把文档结构在内存里表示出来"、用一棵逻辑树表示文档 | Document Object Model |
| MDN(CORS 指南) | fetch 等请求遵守同源策略、服务器需用 Access-Control-Allow-Origin 放行、预检请求的存在 | Cross-Origin Resource Sharing |
| MDN(CDN 词条) | "分布在很多地点的一组服务器、存放数据副本、按距离响应" | CDN |
| MDN(flexbox 基础) | "用 flexbox 要想两条轴" | Basic concepts of flexbox |
| MDN(响应式设计教学) | 响应式设计的目标、媒体查询与 grid / flexbox 的关系 | Responsive design |
| Next.js 官方文档 | 水合的定义("React 把事件处理器接到 DOM 上、让静态 HTML 可交互的过程")、服务端 / 客户端组件的分界、缓存模型(use cache 与旧模型)、Node 最低版本 | Server and Client Components / use cache / Installation |
| React 官方文档 | React 版本号(站内标注 v19.3)、客户端 API hydrateRoot 的存在、组件与 props 的用法 | Thinking in React / hydrateRoot |
| Vite 官方文档 | 它由"开发服务器 + 构建命令"两部分组成、HMR、原生 ES 模块、Node 版本要求 | Getting Started / Why Vite |
| Tailwind CSS 官方文档 | v4 的 Vite 安装方式(@tailwindcss/vite + @import "tailwindcss")、工具类的思路、文档站标注的 v4.3 | Installation with Vite / Styling with utility classes |
| Node.js 官网 / 发行目录 | "异步事件驱动的 JavaScript 运行环境"、LTS 与最新版本号 | About Node.js / dist/index.json |
| npm registry(官方接口) | 各包 latest 版本号与发布时间(文中快照表的直接来源) | registry.npmjs.org/react/latest |
| Next.js 官方博客(16 发布说明) | "Turbopack(稳定):所有应用的默认打包器"、Cache Components 与 use cache 是这一版的新缓存模型 | Next.js 16 |
| TypeScript 官方博客 | 7.0 版本确实已发布("Announcing TypeScript 7.0") | devblogs.microsoft.com/typescript |
证据边界:哪些话会过时,哪些不会
- 会过时:所有版本号、Tailwind 4 的安装步骤、Next.js 的缓存写法(
use cache这套是当前版本的做法,旧文档里的fetch缓存配置被官方标为 "Previous Model")、Next.js 的默认构建工具。看到配置类步骤,先让 AI 核对当前版本。 - 不会过时:三种语言的分工、DOM 是内存里的树、props 与 state 的分界、dev 与 build 的区别、localhost 只有自己能看到、CORS 由服务端放行、密钥不能进前端。这些是概念,换十个框架它们还成立。
- 没写进来、但你可能以为写了:性能优化(打开速度指标、懒加载=用到再下载、代码分割=把大包拆小)、可访问性(无障碍)细则、测试(单元测试、E2E 端到端测试)、状态管理库(Redux / Zustand)、CSS-in-JS(把样式写进 JavaScript 的那个流派)、国际化、以及各家托管平台的计费差异。它们都是正经话题,但都属于"你的项目长到一定程度才需要",硬塞进这篇只会稀释掉上面的地图感。
- 一处刻意的简化:我把"前端"讲成"浏览器里跑的那一半",这是在帮新手建立第一层分界。严格地说,现代框架允许前端代码在服务端执行(§08-7 的服务端组件),所以"前端 = 浏览器里跑的"这句话在工程实践里有例外;本文在需要精确的地方都标注了这个例外。
这篇适合谁、不适合谁
- 适合:在用 AI 写代码、但对前端零基础或只有碎片知识的人;被术语卡住过、想知道"该不该停下来问"的人;想建立一张能长期用的名词地图的人。
- 不适合:想系统学 CSS 或 React 的人(本文只给地图,不给训练);想查某个具体 API 用法的人(用官方文档或让 AI 按文档回答更快);已经能独立做前端项目的人(这篇对你偏浅)。
99一句话总结
前端就是在访客浏览器里跑的那一半;它由四层组成——内容、样式、交互、工具链,外面再套一层"交付";AI 嘴里的每个生词都能被归到其中一层,而你需要立刻紧张的只有三类事:密钥会不会泄露、dev 与 build 有没有搞混、报错属于哪一层。其余的名词,先归类、再追问、用到再学,就够了。
最后,把这篇真正变成你自己的东西,只需要做一个动作:回到 §03,把那三个文件里的内容换成你自己想做的东西——比如一个列着你常看网站的链接页、一个记录你今天做了什么的小卡片。做完之后你再看 AI 说"组件""props""state",它们就不再是一串拼音,而是你亲手碰过的东西的正式名字。