CHAPTER 02 · 起步
环境搭建与第一个项目
目标不是“装好软件”,而是得到一个可复现的环境:你的电脑、同事的电脑、CI、Agent 的沙箱,跑同一个项目得到同样的结果。
- 用版本管理器安装并固定 Node 版本,用 pnpm 管理依赖。
- 读懂一个
tsconfig.json中最关键的 6 个选项。 - 解释为什么
VITE_/NEXT_PUBLIC_前缀的环境变量绝不能放密钥。 - 创建第一个 React + TS 项目,并为它写一份给 Agent 看的
AGENTS.md。
开场:“在我电脑上能跑”
同一个仓库,同事 pnpm dev 一切正常;你这里报 SyntaxError: Unexpected token;Agent 的沙箱里又报 Cannot find module。代码一模一样,为什么结果不同?
答案方向:“代码”只是运行结果的一个输入。另外三个输入是:Node 版本、包管理器及其版本、实际安装的依赖版本(由锁文件决定)。任何一个不一致,结果就可能不同。可复现环境的做法就是把这三样都写进仓库。
第一步:用版本管理器安装 Node 会用
不要直接从官网下载安装包。你迟早会同时维护需要不同 Node 版本的项目。版本管理器让“每个项目自动用它自己的版本”成为可能。推荐 mise(一个工具管理 Node、pnpm、Python、Go 等所有运行时)或 fnm(只管 Node,极简)。
Node 的版本规律:偶数版本会进入长期支持(LTS),奇数版本只是短期的“Current”。生产项目选 LTS。截至 2026 年 9 月,Node 24 是 LTS,Node 26 将在 10 月转为 LTS。
第二步:安装 pnpm 会用
在项目的 package.json 里写上 "packageManager": "pnpm@10.x.x"(写确切版本),很多工具(包括 CI 和部署平台)会据此使用同一个包管理器版本。
最常用的命令,对照你熟悉的工具:
| 做什么 | pnpm | 类比 |
|---|---|---|
| 按锁文件安装全部依赖 | pnpm install | go mod download |
| CI 里严格按锁文件装 | pnpm install --frozen-lockfile | npm ci |
| 添加运行时依赖 | pnpm add zod | go get |
| 添加开发依赖 | pnpm add -D vitest | Maven test scope |
| 运行 scripts 里的命令 | pnpm dev / pnpm run build | make dev |
| 运行依赖里的命令行工具 | pnpm exec tsc(或 pnpm tsc) | — |
| 临时下载并运行一个工具 | pnpm dlx create-vite | npx |
| 看为什么装了某个包 | pnpm why lodash | go mod why |
第三步:编辑器——你最重要的学习工具 会用
VS Code、Cursor、Zed 或 JetBrains WebStorm 都可以。它们内置了 TypeScript 语言服务(language service):你写代码的同时,它在后台持续做类型检查。
把鼠标悬停在任何变量、函数、组件上,编辑器会显示它的完整类型。读 Agent 生成的代码时,这是理解数据形状最快的方法——比读文档快得多。按住 Cmd/Ctrl 点击可以跳到定义,包括 node_modules 里库的类型声明。
建议安装的扩展:Biome(格式化 + Lint,或 ESLint + Prettier)、Tailwind CSS IntelliSense(样式类名提示)、Error Lens(把错误直接显示在代码行尾)。
第四步:安装一个 Coding Agent
本教程不绑定具体工具。主流的终端 Agent 有 Claude Code(Anthropic)、Codex CLI(OpenAI)、pi(开源、极简、可用 TypeScript 扩展)等,它们的工作原理相同(第 12 章会拆开讲)。安装方式各自以官方文档为准,大多支持 npm install -g。
装好后先做一件事:在一个空目录里让它“解释你刚装好的环境”——检查它能否正确运行 node -v、pnpm -v。这能确认 Agent 的执行环境和你的终端一致。
第五步:第一个 TypeScript 程序(不用任何框架)
先不碰 React。用最少的东西感受“类型检查”和“运行”是两件事:
亲手跑一遍这两条命令,比读十遍“类型擦除”的定义都有用。你会看到:运行器很宽容,类型检查器很严格,而你需要的是后者把关。
第六步:创建 LinkNote 的前端骨架
学习阶段,推荐先用 Vite + React 的纯前端模板:概念最少,只有浏览器这一个运行环境,第 06 章学 React 时用它。第 07 章再换成全栈框架。
其他常见的创建命令(以各官网为准):
读懂项目结构
| 路径 | 是什么 | 要不要提交 |
|---|---|---|
src/ | 你的源码。main.tsx 是入口,App.tsx 是根组件 | 是 |
public/ | 原样拷贝到产物的静态文件(图标等) | 是 |
index.html | 浏览器加载的第一个文件,里面引用 src/main.tsx | 是 |
package.json / pnpm-lock.yaml | 依赖声明 / 确切版本 | 是 |
tsconfig*.json | TypeScript 配置 | 是 |
vite.config.ts | 构建工具配置 | 是 |
node_modules/ | 安装的依赖(可随时重建) | 否 |
dist/ | 构建产物 | 否 |
.env / .env.local | 本地环境变量(可能含密钥) | 否(提交 .env.example) |
tsconfig.json:只需要看懂这几项 会用
strict是最重要的一项。关掉它,TS 会退化成“带提示的 JS”,大量错误(如null访问)不会被发现。如果 Agent 为了消除报错去关掉 strict 或加// @ts-ignore,这是需要你拦下来的信号。noUncheckedIndexedAccess默认不开,但强烈建议开:items[0]可能不存在,这类 bug 在生产环境里非常常见。- 如果你打算用 Node 直接运行
.ts,还可以开erasableSyntaxOnly:它禁止使用enum等“需要生成代码”的 TS 语法,保证类型可以被简单地剥离。
环境变量:哪些会被送到浏览器 原理
这是新手最容易犯、后果最严重的错误之一,所以即使在“搭环境”这一章,也要讲到原理层。
后端开发中,环境变量只存在于服务器进程里,天然是保密的。但在前端构建中,打包器会在构建时把代码里引用的某些环境变量直接替换成字面值,写进发给浏览器的 JS 文件里。为了防止误泄露,各框架规定:只有带特定前缀的变量才会被这样处理。
带 VITE_ / NEXT_PUBLIC_ 等公开前缀的变量,等同于公开发布。API Key、数据库连接串绝不能用这类前缀。如果浏览器端需要调用 LLM,正确做法是浏览器请求你自己的服务器,由服务器带着密钥去调用(第 11 章)。
第七步:给 Agent 的项目说明书
几乎所有 Coding Agent 都会在启动时读取项目根目录的说明文件:AGENTS.md(跨工具的通用约定)或 CLAUDE.md(Claude Code)。它相当于你给新同事的“入职文档”。项目一创建就写,内容越具体越好:
第 13 章会详细讲怎么写好这份文件。现在记住一个原则:凡是你发现自己第二次对 Agent 说同一句话,就把它写进 AGENTS.md。
常见报错速查
| 报错 | 通常原因 | 处理 |
|---|---|---|
command not found: pnpm | 没装,或 shell 没加载版本管理器 | 重开终端;检查 mise/fnm 的 shell 集成 |
Cannot find module 'x' | 没安装依赖,或 pnpm 严格模式下没声明该依赖 | pnpm install;或 pnpm add x |
ERR_REQUIRE_ESM / Cannot use import statement outside a module | ES 模块与 CommonJS 混用(第 03 章) | 检查 "type": "module" 和文件扩展名 |
EADDRINUSE: address already in use :::5173 | 端口被占用(常见于上一个 dev server 没关) | 关掉旧进程或换端口 |
Unsupported engine | Node 版本不满足项目要求 | 按项目的 mise.toml / .nvmrc 切换版本 |
pnpm dev 报错(完整输出如下)。先不要改代码:请先告诉我 Node 版本、pnpm 版本、锁文件是否与 package.json 一致,判断是环境问题还是代码问题,再给修复方案。”先诊断、后修改,是避免 Agent “为了让报错消失而乱改”的最有效方法。
自检
1. 你在 .env 里写了 VITE_OPENAI_KEY=sk-...,只在一个“设置”页面用到它,页面还加了登录保护。安全吗?
不安全。VITE_ 前缀的变量在构建时被内联进 JS 文件。JS 文件是静态资源,不需要登录就能下载(登录保护的是页面数据,不是打包后的脚本)。任何人打开 JS 文件都能搜到这个 key。应该去掉前缀,只在服务器端使用。
2. 为什么锁文件必须提交,而 node_modules 不能提交?
锁文件记录了每个依赖(包括间接依赖)的确切版本和校验值,是“可复现”的关键;它很小,而且是文本。node_modules 可以随时从锁文件重建,体积巨大,还可能包含和操作系统相关的二进制文件。
3. Agent 为了解决一个类型错误,在 tsconfig 里把 strict 改成了 false,然后说“类型检查通过了”。你怎么评价?
这是“让检查闭嘴”而不是“修复问题”。关闭 strict 会让大量真实缺陷(如 null 访问)不再被报告。应该回退这个修改,要求 Agent 在 strict 模式下修复具体错误,并把“不得修改 tsconfig 严格选项”写进 AGENTS.md。