🟢 入门

前端名词地图: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

5
前端从文件到上线,只有 5 层
41
个名词在速查表里随时可回查
17
组最容易被混为一谈的名词
24
段可直接复制去跑的代码 / 命令 / 提示词

0030 秒速览:三句话,然后决定你从哪一节开始

🎯 读完你能做什么

当 AI 对你说「这是个客户端组件(需要在浏览器里跑的那种组件,§06 讲),加个 use client,然后重新 build 一下(把源码加工成浏览器能跑的文件,§05 讲),注意别触发 hydration mismatch」时,你能听懂这句话里每一个词分别属于哪一层、它要你做什么、以及哪一句值得你停下来追问。你不需要因此会写前端代码——你需要的是不再被术语挡住判断。

如果只记三句话

  1. 前端 = 在访客的浏览器里跑的那一半。后端 = 在服务器上跑的那一半。凡是"别人一打开网页就该发生的事",都属于前端。所以前端的问题,往往只有"换台电脑 / 换个网络 / 换个浏览器"才暴露出来。
  2. AI 嘴里的前端词,九成落在四层里:内容(HTML)、样式(CSS)、交互(JavaScript 与框架)、工具链(Node / npm / 打包)。这四层再套上外面那一层"交付"(部署、域名、缓存、跨域),就是本文的整张地图。听到一个生词,第一反应不该是"这什么",而是"它属于哪一层"——定层之后,你基本能猜出它大概管什么。
  3. 看见页面 ≠ 页面能用。服务器先把一份"死的"页面发到浏览器(这就是 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.js24.21 LTS(Long Term Support,长期支持版:官方会持续修安全问题的那个档位,新项目该选它;"Krypton" 是它的代号),最新线 26.8工具链底座。只要 AI 让你敲 npm 开头的命令,就用到它
npm随 Node 一起装装包的默认工具。包管理器还有 pnpm / yarn / bun
React19.3交互层最流行的"库"。组件、props、state、hook 都出自这里
Next.js16.3.5盖在 React 上的"框架"。SSR / SSG / 水合这些词最常在这里出现
Vite8.3本地开发服务器 + 打包工具。跑 npm run dev 时在后面干活的多半是它
Tailwind CSS4.3样式层的一种写法:把样式拆成一个个小类名
TypeScript7.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)不是一种技术,而是"前端后端都能自己写"的人或项目。

从你在地址栏敲下网址,到看见页面,中间发生了什么

1浏览器拿域名去问 DNS:这个网址对应哪台机器
→
2向那台机器发一个请求(HTTP:浏览器和网站之间约定好的那套对话格式)
→
3对方回一份 HTML:页面的骨架
→
4浏览器继续要 CSS 和 JavaScript 文件
→
5执行 JS,页面变成可交互(这一步可能叫"水合")
→
6你再点按钮,才开始向 API 要新数据

这六步里,第 3~6 步基本都在前端的地盘上。理解了这条链,很多术语就能自动归位:HTML / CSS / JavaScript 管第 4 步,构建工具管"为什么源码不能直接用、要先生成这些文件",SSR / SSG 管第 3 步的 HTML 是谁在什么时候生成的,水合管第 5 步。

"客户端 / 浏览器端 / 前端"是同一件事吗

基本是,但语气不同:

为什么这个区分值钱?因为前端代码是"公开"的。任何访客都能按 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 就是"链接指向哪里"这个属性。

这个类比到哪里就不准了

类比必须说边界,否则你会用它推出错的结论:

后缀名认亲:AI 生成的文件到底在写哪种语言

后缀是什么AI 为什么用它 / 你该怎么看它
.html纯 HTML 页面最早的形态。单独一个就能双击打开
.css纯样式表被 HTML 引用;也常被框架的入口文件一次性引入
.js纯 JavaScript浏览器直接能跑
.jsxJavaScript + 类 HTML 写法(JSX)写 React 用。浏览器看不懂,需要先"翻译"(这就是构建,见 §05)
.tsTypeScript:给 JavaScript 加了"类型"检查浏览器同样看不懂,需要翻译。类型就是给每个数据贴上标签(这是字符串、这是数字),写错时提前报错
.tsxTypeScript + JSXReact + TypeScript 项目的默认后缀,AI 生成现代前端项目时最常见
.vue / .svelte另外两个框架(Vue、Svelte)的单文件格式长相和能力都类似 .tsx:一个文件里装下一个组件的长相、逻辑、样式
.json纯数据格式,键值对package.json 这类配置文件就是它;本身不含任何逻辑
💡 一句能省很多事的判断

看到 .ts / .jsx / .vue 这类后缀,你就知道这个项目必须要有一台"翻译机"才能跑,也就必然会有 package.json、必然要先 npm install。看到纯 .html,它可能双击就能跑。这条判断能让你在打开一个陌生项目时先知道"我要不要先装东西"。

03最小例子:三个文件,不装任何东西就能跑

这一节请真的动手做一遍,五分钟。做完你会同时获得三样东西:一个能跑的网页、"三种语言各管什么"的手感、以及后面所有术语的落脚点。

1
新建一个文件夹,比如 my-first-page

用你顺手的任何方式:Finder 里右键新建文件夹、VS Code 里"打开文件夹"、或者终端里 mkdir my-first-page && cd my-first-page。没有"必须用哪个工具"这回事。

2
在这个文件夹里建 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(行为)拉了进来。所以接下来那两个文件的名字不能拼错,否则网页还能打开,但样式和点击会静默失效——这是新手第一次遇到的"我明明写了但没生效"。

3
建 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; }   /* 鼠标移上去变深一点 */
4
建 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);后面那个函数体,是浏览器在每次点击时替你调用的回调。

5
双击 index.html,看它跑起来

页面应该是深色背景、中间一行标题、一个紫色按钮。点一下按钮,下面那行字从 0 变成 1。这就是一个完整的网页了——没有 Node、没有 npm、没有框架。

6
反过来验证一次:三个文件各改一处

① 把 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 常说的那句就通了:

🧩 翻译一句 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 的关系你该用哪个
npmNode 自带的包管理器,最通用基准没特别理由就用它,教程都以它为准
npxnpm 附带的"临时执行器"不是替代品,是补充跑一次性的工具命令
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: nodeNode 没装,或装了但终端找不到(装完要重开终端)去 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)是构建工具在你电脑上临时开的一个小网站。它做三件事:

那个终端窗口不能关——关了服务器就停了,网页立刻打不开。这就是"为什么 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 是框架",这不是抠字眼:

所以"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)",很多人会理解成"页面重新加载"。不是:

1你调用了 setN,状态变了
→
2React 重新调用一次这个组件函数
→
3拿到一份新的"界面应该长什么样"的描述
→
4和上一次对比,只改真正变了的那几个地方

注意第 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

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 自己换内容。

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 提出的一种折中:页面仍是静态生成的、发得飞快,但你给它定一个"保质期";过期后第一个访问的人会看到旧页面,同时服务器在后台悄悄生成新的一份,后面的人就拿到新的了。

水合(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 会频繁提到这个分界:

所以看到报错 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 时开门见山:用它要想的是两条轴。

➡️
flex:一行(或一列)里怎么排
适合"一排东西":导航栏、按钮组、卡片列表。主轴决定它们是横着排还是竖着排,交叉轴决定它们在另一个方向上怎么对齐。
.row {
  display: flex;
  gap: 12px;              /* 子元素间距 */
  align-items: center;    /* 交叉轴居中 */
  justify-content: space-between; /* 主轴两端对齐 */
  flex-wrap: wrap;        /* 装不下就换行 */
}
▦
grid:先划格子,再往里放
适合"一张表":相册、仪表盘、卡片墙。你先把页面切成几列几行,再指定谁占哪几格。
.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 变量统一主题",说的就是上面这件事。

"样式不生效"的排查顺序

这是前端最高频的一类问题,而且有一条固定的排查顺序,照着走能省下大量瞎改:

1
先确认这个文件真的被用上了

CSS 有没有被引入?Tailwind 项目里,你写的新文件在不在它扫描的目录里?改配置之后 dev server 有没有重启过?

2
看类名有没有拼错

Tailwind 的类名拼错不会报错,只是没效果——这是它最坑的地方。items-center 写成 item-center,你只会看到"没居中"。

3
F12 看这条样式有没有被划掉

Elements 面板里选中那个元素,右下角 Styles 里被划横线的规则表示被别的规则覆盖了。这叫优先级(specificity):越具体的选择器赢;!important 是强行插队的写法,能不用就别用。

4
最后才怀疑浏览器缓存

改了半天没变,硬刷新一次(Mac Cmd+Shift+R / Windows Ctrl+F5)。但这一步要放在最后——一开始就怪缓存,会让你错过真正的错因。

10从 localhost 到别人能看到:部署、环境变量、CORS

localhost:3000 是什么,为什么别人打不开

localhost 是"这台机器自己"的固定名字(技术上对应 127.0.0.1,叫回环地址),3000 是端口——一台机器上可以有很多程序同时在提供网页服务,端口就是区分它们的门牌号。所以:

⚠️ 一句自查

打开浏览器地址栏看一眼:如果它写的是 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 三个词

环境变量与 .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 的解释可以拆成三句人话:

  1. 网页里发请求(fetch 是浏览器自带的"要数据"函数,XMLHttpRequest 是它的老前辈)时,浏览器默认遵守同源策略(same-origin policy):只能向"同一个来源"要数据。来源由三样组成——协议 + 域名 + 端口,三者有一样不同就算"跨域"。localhost:5173 和 localhost:3000 端口不同,也算跨域。
  2. CORS(Cross-Origin Resource Sharing,跨源资源共享)是打破这道墙的正规方式:需要服务器在响应头(服务器回给你的那份"附加说明",和正文一起发回来)里明确说"我允许这个来源来拿"(Access-Control-Allow-Origin)。有些请求还会先发一个试探性的预检请求(preflight),得到允许才发真请求。
  3. 所以:这个错是服务器没放行,不是你前端代码写错了。
✅ 遇到 CORS 报错时,正确的第一句话

不要对 AI 说"帮我修这个跨域错误"——那会让它在前端乱试。说:"这个接口没有返回 CORS 允许头。请告诉我该在服务端加什么响应头,或者有没有必要走服务端代理转发。"把球踢回正确的一侧,通常五分钟就解决;让 AI 在前端硬改,可能耗一下午。

CDN 与缓存:为什么"我改了,线上还是旧的"

MDN 对 CDN(Content Delivery Network,内容分发网络)的说明是:它是分布在很多地点的一组服务器,这些服务器存放数据的副本,从而让离用户最近的那台来响应请求。用人话讲:把你的静态文件复印很多份放到各地,谁离哪近就从哪拿。

CDN 靠缓存(cache,把文件先存一份,下次直接给)来提速,而缓存正是"改了代码线上没变"的头号嫌疑人。按这个顺序排查:

1
先确认你真的部署了新版本

本地跑过 npm run build 了吗?部署平台的那次构建成功了吗?很多"缓存问题"其实是那一次构建失败了,或者压根没触发。

2
再确认你看到的是自己浏览器的旧缓存

硬刷新(Cmd+Shift+R / Ctrl+F5)、或无痕窗口打开。如果无痕窗口是新的,那就是你本地缓存。

3
最后才是平台 / CDN 缓存

托管平台的"清除缓存 / 重新部署",或者等它自己的缓存过期。这一步涉及的就是 §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六个常见误解(以及它们为什么那么诱人)

误解 1:前端就是让页面好看。
它诱人是因为你看到的第一层确实只有"好不好看"。但前端真正在管的是可用与正确:手机上能不能点、表单提交后状态对不对、页面显示的数字和后端数据一致不一致。所以"样式"只是它最外层的一部分,"好看但用不了"是最常见的失败形态。
误解 2:AI 写的前端代码我看不懂也没关系,能跑就行。
短期成立,长期有代价。你可以不懂实现,但有三类事必须能看懂:① 密钥有没有跑到前端;② 改动会不会把别人的数据/样式改坏;③ 报错属于哪一层(§11 的三步法就是为这个)。"完全看不懂"的项目,一旦出错你就只能反复重试,而重试是 AI 编程里最贵的动作。
误解 3:报错就是代码写错了。
这是新手最大的时间黑洞。前端报错至少分四类:环境问题(依赖没装、Node 版本低)、用法问题(hook 放进条件里、该标 "use client")、配置问题(CORS、路径别名)、真正的逻辑 bug。四类的修法完全不同,而前三类改业务代码是纯浪费——§04-6 那张表就是为前三类准备的。
误解 4:我本地能打开,就是做好了。
诱人是因为它"真的能看到"。但地址栏里的 localhost 意味着这个"服务器"就是你自己。真正的验收标准是:用别人的手机、别人的网络打开一个真实网址,点一遍主要功能。这也顺带解释了为什么很多问题只在部署后才冒出来(路由配置、环境变量、缓存策略在本地都被绕过了)。
误解 5:我改了代码,线上就会自动变。
只有走完"构建 + 部署"才会变。静态页面的 HTML 是构建时生成的(§08-3),所以改了源码但没重新构建,线上还是旧的;用了 ISR 的页面还会有一个"保质期",过期前看到旧内容属于设计行为。记住那个顺序:改代码 → npm run build → 部署 → 再刷新看。
误解 6:hydration 报错是警告,先上线再说。
它确实是"警告"语气,但后果是这一个区域会失去交互:页面看着正常,按钮却点不动,或者输入的内容突然被重置——对用户来说就是"这网站坏了"。而且它常常只在特定条件下出现(时间、随机数、不同设备),所以更难被发现。看到它就该处理,别攒着。

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.3Installation 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

证据边界:哪些话会过时,哪些不会

这篇适合谁、不适合谁

99一句话总结

🎯 核心要点

前端就是在访客浏览器里跑的那一半;它由四层组成——内容、样式、交互、工具链,外面再套一层"交付";AI 嘴里的每个生词都能被归到其中一层,而你需要立刻紧张的只有三类事:密钥会不会泄露、dev 与 build 有没有搞混、报错属于哪一层。其余的名词,先归类、再追问、用到再学,就够了。

最后,把这篇真正变成你自己的东西,只需要做一个动作:回到 §03,把那三个文件里的内容换成你自己想做的东西——比如一个列着你常看网站的链接页、一个记录你今天做了什么的小卡片。做完之后你再看 AI 说"组件""props""state",它们就不再是一串拼音,而是你亲手碰过的东西的正式名字。

⌂