CHAPTER 02 · 起步

环境搭建与第一个项目

目标不是“装好软件”,而是得到一个可复现的环境:你的电脑、同事的电脑、CI、Agent 的沙箱,跑同一个项目得到同样的结果。

⏱ 约 45 分钟(含动手)会用环境变量暴露规则是 原理
读完本章,你应该能够:
  • 用版本管理器安装并固定 Node 版本,用 pnpm 管理依赖。
  • 读懂一个 tsconfig.json 中最关键的 6 个选项。
  • 解释为什么 VITE_ / NEXT_PUBLIC_ 前缀的环境变量绝不能放密钥。
  • 创建第一个 React + TS 项目,并为它写一份给 Agent 看的 AGENTS.md。

开场:“在我电脑上能跑”

开场问题

同一个仓库,同事 pnpm dev 一切正常;你这里报 SyntaxError: Unexpected token;Agent 的沙箱里又报 Cannot find module。代码一模一样,为什么结果不同?

答案方向:“代码”只是运行结果的一个输入。另外三个输入是:Node 版本、包管理器及其版本、实际安装的依赖版本(由锁文件决定)。任何一个不一致,结果就可能不同。可复现环境的做法就是把这三样都写进仓库。

Node 版本 .nvmrc / mise.toml "engines" 字段 包管理器 "packageManager" pnpm@10.x 依赖的确切版本 pnpm-lock.yaml 必须提交到 Git 你的代码 + 配置 src/ · tsconfig.json .env.example(不含真值) 相同的运行结果(本机 / 同事 / CI / Agent)
四个输入都写进仓库,环境才可复现。唯一不能进仓库的是真实密钥,它们通过环境变量单独注入。

第一步:用版本管理器安装 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 installgo mod download
CI 里严格按锁文件装pnpm install --frozen-lockfilenpm ci
添加运行时依赖pnpm add zodgo get
添加开发依赖pnpm add -D vitestMaven test scope
运行 scripts 里的命令pnpm dev / pnpm run buildmake dev
运行依赖里的命令行工具pnpm exec tsc(或 pnpm tsc)—
临时下载并运行一个工具pnpm dlx create-vitenpx
看为什么装了某个包pnpm why lodashgo 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*.jsonTypeScript 配置是
vite.config.ts构建工具配置是
node_modules/安装的依赖(可随时重建)否
dist/构建产物否
.env / .env.local本地环境变量(可能含密钥)否(提交 .env.example)

tsconfig.json:只需要看懂这几项 会用

环境变量:哪些会被送到浏览器 原理

这是新手最容易犯、后果最严重的错误之一,所以即使在“搭环境”这一章,也要讲到原理层。

后端开发中,环境变量只存在于服务器进程里,天然是保密的。但在前端构建中,打包器会在构建时把代码里引用的某些环境变量直接替换成字面值,写进发给浏览器的 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 moduleES 模块与 CommonJS 混用(第 03 章)检查 "type": "module" 和文件扩展名
EADDRINUSE: address already in use :::5173端口被占用(常见于上一个 dev server 没关)关掉旧进程或换端口
Unsupported engineNode 版本不满足项目要求按项目的 mise.toml / .nvmrc 切换版本
对 Agent 怎么说
模糊
精确
“项目跑不起来,帮我修。”
“运行 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。