项目:vitest-playground | 版本:v1.2 | 生成日期:2026-10-06 | 依据:源码分析
本篇对 package.json 的全部依赖做一次"逐个点名":每个依赖是什么、被谁用、为什么选它、有什么替代方案、升级时会踩什么坑。与《14-配置与环境详解》§4 的分工是:14 篇把 package.json 当"配置文件"逐字段解析(scripts、engines、packageManager 等语义),本篇把 dependencies/devDependencies 当"供应链清单"做工程评估——前者回答"配置写了什么",本篇回答"这些包选得对不对、养起来累不累"。安装事实方面,本仓库存在 pnpm-lock.yaml(CI 以 pnpm install --frozen-lockfile 安装,见 .github/workflows/ci.yml),.npmrc 内容为 legacy-peer-deps=true,pnpm-workspace.yaml 仅含 allowBuilds: esbuild: true 放行配置——这些"周边配置"的逐条解释见《14》§8,本篇只在涉及依赖行为时引用。
先交代两个贯穿全篇的版本事实。其一,本仓依赖版本分两种策略:运行器核心三件套(vitest / @vitest/coverage-v8 / @vitest/ui)钉精确版本 4.1.8,其余全部用 caret 范围(^)。钉精确版本的原因写在《00-项目总览》§3 与 ADR-004/ADR-007 的实现细节里——运行器直接 spawn node_modules/vitest/vitest.mjs,且 parseBenchTable 的注释明确针对 "Vitest 4.1.8 的 bench 内置 reporter 只有 default/verbose" 这一版本事实,minor 漂移可能同时改变 CLI 行为与解析正则的适配对象,因此三者必须同版本同进退。其二,本机 node_modules 的实际解析结果(pnpm install 后读取各包 package.json 确认):vitest 4.1.8、vite 6.4.3、react 19.2.8、antd 6.6.2、monaco-editor 0.52.2、happy-dom 20.12.0——caret 范围全部解析到当前最新补丁,与 lockfile 共同锁死。
| 依赖 | 版本范围 | 实际解析 | 用途 | 使用位置 | 选型理由 | 替代方案 | 升级风险 |
|---|---|---|---|---|---|---|---|
| react | ^19.2.5 | 19.2.8 | UI 框架 | 全部页面组件 | 课时本身教 React 测试,生态与 RTL 成熟 | Preact/Solid(生态不匹配) | 大版本 API 变化;与 antd 兼容矩阵 |
| react-dom | ^19.2.5 | 19.2.8 | React 渲染器 | src/main.tsx | 与 react 同源同版本 | - | 必须与 react 同步升 |
| react-router-dom | ^7.1.0 | 7.x | 声明式路由 | src/app.tsx、chapter.tsx | createBrowserRouter 支持章节/课时两级动态参数 | TanStack Router、wouter | v6→v7 已有一次迁移;API 相对稳定 |
| antd | ^6.4.3 | 6.6.2 | 组件库 | AppLayout/chapter/index/progressPage | 表单/进度条/Tabs/消息现成可用 | MUI、Arco、自研 | 大版本破坏性样式;React 19 兼容 |
| @ant-design/icons | ^6.2.3 | 6.x | 图标集 | 各页面图标 | 与 antd 同族配套 | 独立图标库 | 随 antd 主版本联动 |
| monaco-editor | ^0.52.2 | 0.52.2 | 编辑器内核(ESM 深路径导入) | monacoSetup.ts | VS Code 同源编辑器,markers/补全质量最高 | CodeMirror 6 | 0.x 版本 minor 即可破坏 ESM 深路径 |
| @monaco-editor/react | ^4.7.0 | 4.x | Monaco 的 React 封装 | CodeEditor.tsx | 声明式挂载 + loader 注入本地实例 | 直接手写挂载 | loader 默认 CDN 行为需覆盖(已做) |
| 依赖 | 版本范围 | 实际解析 | 用途 | 使用位置 | 选型理由 | 替代方案 | 升级风险 |
|---|---|---|---|---|---|---|---|
| vitest | 4.1.8(精确) | 4.1.8 | 产品运行引擎 + 自测框架 | vitestServerRunner.ts spawn、vitest.config.ts | 被教对象与执行引擎同源 | Jest(行为漂移大) | 钉版即风险锁死;升级需跑 check:lessons |
| @vitest/coverage-v8 | 4.1.8(精确) | 4.1.8 | v8 覆盖率 provider | 沙箱 coverage 配置、vitest.config.ts | 与 vitest 严格同版 | c8 裸用 | 必须与 vitest 同步 |
| @vitest/ui | 4.1.8(精确) | 4.1.8 | 自测的可视化面板 | pnpm test:ui | 开发体验 | - | 必须与 vitest 同步 |
| vite | ^6.0.0 | 6.4.3 | 构建/dev/preview 服务器 + 中间件宿主 | vite.config.ts | 插件体系可注入自定义中间件 | Rspack/Webpack | 大版本插件 API 变化(见 ADR-001) |
| @vitejs/plugin-react | ^4.3.4 | 4.x | React Fast Refresh/JSX 转换 | vite.config.ts plugins | 官方配套 | @vitejs/plugin-react-swc | 随 vite 大版本联动 |
| typescript | ^6.0.3 | 6.x | 全仓类型 + pnpm tsc 门禁 | tsconfig、CI | strict 模式守前后端共享类型 | - | TS6 为新大版本,编译行为或有变化 ⚠️ 待确认 |
| happy-dom | ^20.10.1 | 20.12.0 | 浏览器 DOM 模拟环境 | 沙箱 pragma、vitest.config.ts | 比 jsdom 快、维护活跃 | jsdom | DOM 行为差异需集成测试兜底 |
| @testing-library/react | ^16.3.2 | 16.x | React 组件测试(第十五章 grader 用) | jsdom 课时 grader、tests | React 官方推荐查询式断言 | Enzyme(已停滞) | 与 React 大版本兼容矩阵 |
| @testing-library/dom | ^10.4.1 | 10.x | RTL 的底层查询库 | 传递依赖(显式声明) | peer 依赖显式化 | - | 跟随 RTL |
| @testing-library/jest-dom | ^6.9.1 | 6.x | DOM 断言匹配器 | 沙箱 setup.ts 注入 | toBeInTheDocument 等可读匹配器 | 手写断言 | /vitest 入口路径随版本变化 |
| @types/node | ^25.9.1 | 25.x | Node 类型声明 | runner/脚本 | child_process 等类型 | - | 与 engines(node>=22) 不对齐(见 §4) |
| @types/react | ^19.2.14 | 19.x | React 类型 | 全部 TSX | 官方类型 | - | 跟随 React |
| @types/react-dom | ^19.2.3 | 19.x | ReactDOM 类型 | main.tsx 等 | 官方类型 | - | 跟随 React |
一个先要澄清的观察:依赖面异常克制。运行时依赖只有 7 个,且没有任何后端框架、状态管理库、HTTP 客户端库(fetch 原生)、样式方案库(CSS Modules 原生)。这份克制不是偶然——ADR-001(复用 Vite 服务器当后端)、ADR-009(localStorage 当数据库)在架构层就消灭了大部分依赖需求。依赖数量少直接降低了供应链与升级面,这是本仓健康度的第一支柱。
vitest 在本项目的角色有三个层次,理解层次差异才能理解依赖策略。第一层是产品能力:vitestServerRunner.ts 第 19 行把 node_modules/vitest/vitest.mjs 拼成绝对路径,spawn 子进程执行 run 或 bench 子命令——这是"用 CLI 而非编程式 API"的关键选择。Vitest 官方提供 createVitest() 编程式入口,可以让运行器在进程内直接起测试,省掉子进程冷启动;但进程内运行意味着用户代码与 Vite 服务器共享堆、事件循环与全局状态,一次死循环就能挂掉服务器本身,与 ADR-003/005 的隔离目标根本冲突。spawn CLI 的代价(每请求数百毫秒冷启动)在《10-性能设计与优化》§1 的耗时构成里被论证为可接受。第二层是版本钉死的理由:运行器对 vitest 的依赖不止"能跑",还包括三处版本敏感行为——--outputFile.json=result.json 的 jest 兼容报告格式(parseVitestJson 按其字段解析)、bench 默认 reporter 的表格列序(parseBenchTable 的行正则 /^\s*[·✓×]\s+(.+?)\s{2,}... 按列位置取数)、--root 向上解析 node_modules 的行为。三者任何一项在 minor 版本变化都会静默破坏产品,因此三件套(vitest/coverage-v8/ui)钉 4.1.8 是"把升级变成显式决策"的防御。第三层是自测框架:vitest.config.ts 以 happy-dom + globals + setupFiles 配置项目自身测试,include 只圈 src/pages/vitest-learn/** 与 tests/**(注释原话:避免遗留 Ant Design Pro 模板文件干扰)。同一依赖既是课程教的内容又是测试自己工具,版本一致性让"教程里讲的行为"与"CI 里的行为"天然同步——这是本项目独有的选型红利,任何替代框架(Jest 等)都会破坏这一点。升级 vitest 时必须四绿(tsc/test/build/check:lessons,见《06-部署运行》§10)之外,重点回归 runner-security 集成用例与 bench 解析正则。
这对组合的分工是:monaco-editor 提供编辑器内核,@monaco-editor/react 提供 React 声明式封装(<Editor /> 组件、loading 占位、受控 value)。选型理由在 ADR-008 已详述——markers 行内波浪线、智能补全、多模型(可编辑主编辑器/只读 grader/只读 extraFiles 三形态复用)都是 CodeMirror 需要额外插件才能追平的能力。这里的重点讲依赖管理层面的三个动作。其一,绕过 CDN:@monaco-editor/react 默认经 loader 从 CDN 拉取 monaco,monacoSetup.ts 第 38 行 loader.config({ monaco }) 用本地 ESM 实例覆盖,配合 ADR-002 的离线目标。其二,深路径裁剪:monacoSetup.ts 只 import monaco-editor/esm/vs/editor/editor.api 与 typescript 两个 contribution,不碰 json/css/html 语言包——注意这依赖 monaco-editor 的 ESM 目录结构(esm/vs/... 深路径),0.x 语义化版本下 ^0.52.2 实际只解析到 0.52.x,但任何一次 minor 升级都可能重排内部路径,所以表里把"深路径破坏"列为 monaco 的主要升级风险;好在 monaco 本身已停止激进的 breaking change(0.x 号持续多年),实际风险可控。其三,worker 的同源加载:MonacoEnvironment.getWorker 以 ?worker 导入让 Vite 把 tsWorker/editorWorker 打成独立 worker chunk,COEP 头(ADR-012)保证了同源 worker 加载不跨域。若未来升级 @monaco-editor/react 5.x(⚠️ 待确认其存在与否),需重验 loader 配置行为。
第十五章的 React 组件测试依赖四个包的协作,链路是:课时数据声明 environment: 'jsdom' → buildLessonFiles 给 spec 文件注入 // @vitest-environment happy-dom pragma(注意:pragma 字符串写的是 happy-dom 而非 jsdom,data.ts 第 19 行注释里的 "jsdom" 字样是字段名的沿用,真实指令以 vitestServerRunner.ts 第 75 行为准)→ vitest 在沙箱内以 happy-dom 模拟 DOM → grader 里 @testing-library/react 的 render/screen/fireEvent 操作这个模拟 DOM → @testing-library/jest-dom/vitest(沙箱 setup.ts 注入)提供 toBeInTheDocument 类可读匹配器。选 happy-dom 而非经典 jsdom 的理由是社区共识的"更快、维护更活跃",且 vitest 官方文档以其为一等公民环境;代价是 DOM 行为的边角差异(如部分事件与布局语义)可能让个别课时断言在不同版本表现不一——本仓的防线是集成测试真跑全部形态课时(runner-security 与 check:lessons),升 happy-dom 后这两条门禁就是验收标准。三件套中 @testing-library/dom 是 RTL 的 peer 底层,显式写入 devDependencies 是 RTL v16 时代的推荐做法(避免 peer 解析问题,与 .npmrc 的 legacy-peer-deps=true 一起构成双层保险——注意这个 npmrc 开关是历史遗留的宽松项,新装依赖时可能掩盖 peer 冲突 ⚠️ 待确认是否有移除计划)。@testing-library/jest-dom 的升级风险集中在导入入口:v6 的 /vitest 子路径导出是类型与运行时注人的正门,未来大版本若改名会破坏 writeRunConfig 写出的 setup.ts 一行——它是沙箱配置里唯一引用第三方包名的地方(vitestServerRunner.ts 第 126 行),改了它等于改了所有运行。
AGENTS.md 记载项目从 Ant Design Pro(Umi Max)模板剥离而来,只保留组件库。antd 承担了 AppLayout 导航、chapter 工作台的 Tabs/Switch/Breadcrumb/Progress、index 首页卡片、progressPage 的统计块,以及全局 message 反馈;icons 提供 9 个页内图标。选型延续模板惯性 + 组件覆盖面够用,替代方案(MUI/Arco/自研)在"已剥离模板"的现状下迁移成本高于收益,不是真实候选。值得记录的是两处适配成本:React 19 与 antd 的兼容性(antd 6.x 官方支持 React 19,本仓 react 19.2.8 + antd 6.6.2 的组合实测工作正常,但 message.success 这类命令式 API 在 React 19 并发渲染下的行为差异值得留意 ⚠️ 待确认);chapter.tsx 的 Alert 用了 title prop(antd 6 新 API,v5 时代是 message prop)——这提示 antd 从 v5 升 v6 时页面代码已做过一次适配,接手者不要按 v5 文档理解这些 JSX。升级风险主要是样式体系(CSS-in-JS token)与主题联动:src/theme.tsx 的明暗主题目前通过 CSS 变量 + Monaco 主题切换实现,antd 自身的暗色算法主题并未接入(⚠️ 待确认是否为有意省略),若未来引入 antd ConfigProvider theme token,需要与现有 CSS 变量方案对齐。
vite 是唯一"身兼三职"的依赖:构建器(pnpm build → dist/)、开发服务器(pnpm dev :8000)、生产预览服务器兼 API 宿主(pnpm preview --port 8000,中间件在此挂载)。ADR-001 论证了为什么复用它当后端;依赖视角的要点是升级联动:configureServer/configurePreviewServer/server.middlewares.use 这三个 API 是 connect 风格的稳定面,vite 6 → 7 的大版本(⚠️ 待确认届时 API 形态)是本项目最大的单点升级风险——它同时影响构建产物、开发体验与生产运行时。@vitejs/plugin-react 只在 dev/构建期起作用(JSX 转换 + Fast Refresh),与生产 API 无关,升级风险低。另外 resolve.alias 的 @/、@root/ 两个别名在 vitest.config.ts 与 tsconfig.json 三处重复声明(《14》§5 有逐项解析),升级时三处需同步,这是依赖间接造成的配置冗余。
typescript ^6.0.3 承担两件事:pnpm tsc --noEmit 的 CI 门禁(strict 全仓检查)与编辑器内 Monaco TS worker 的语言服务(monacoSetup.ts 的 compilerOptions 独立设置,不读 tsconfig)。注意 TS 6 是新的大版本线(⚠️ 待确认本仓实际解析版本与 TS6 正式版的对应关系),若上游有编译期行为收紧,第一信号通常是 pnpm tsc 变红而非运行时错误——门禁反而是最早报警器。@types 侧有一个值得立项的不一致:engines.node: ">=22.0.0" 声明运行时为 Node 22+,而 @types/node 是 ^25.9.1(Node 25 的类型声明)——类型与运行时的主版本不对齐,意味着 runner 里 child_process API 的类型面可能包含运行时实际不存在的新签名,TypeScript 查不出来的"类型说有、运行时没有"只能靠测试兜底。建议将 @types/node 对齐到 22.x 线(或明确记录为何用 25),这是本篇健康度评估里少数可立即执行的改进项。@types/react/react-dom 跟随 React 19 线,无独立风险。
scripts/check-lessons.ts 需要一个 esbuild 可执行文件把 data.ts bundle 成临时 mjs(原因见《05-核心流程》流程六),但它不在 package.json 依赖清单里——而是运行时从 node_modules/.pnpm 目录探测 esbuild@ 版本取二进制。esbuild 是 vite 的传递依赖,这个做法把"间接依赖当直接依赖用",属于隐式契约:vite 未来若更换转换器(或 pnpm 目录布局变化),check:lessons 会静默失效。pnpm-workspace.yaml 的 allowBuilds: esbuild: true 正是为此放行 esbuild 的 postinstall 二进制下载。这不是要立即修的问题,但接手者应知道这层脆弱性存在于依赖图深处(⚠️ 待确认是否有更稳妥的探测实现)。
综合以上逐个分析,给出五个维度的评估结论。供应链面(优):运行时依赖 7 个、全仓直接依赖 20 个,无任何跨生态(如同时引 axios + fetch 封装)或功能重叠依赖;关键执行引擎 vitest 钉精确版本,lockfile + --frozen-lockfile 保证 CI 与本地一致。版本策略(良,有一处不一致):三件套精确钉版、其余 caret 的双轨制符合"核心稳定、边缘跟进"的原则;但 @types/node(25) 与 engines(>=22) 的错位是明确的版本卫生问题,建议对齐。升级路径(中):最重的升级是 vite 大版本(中间件宿主)与 React/antd 联动(兼容矩阵),vitest 升级必须同步验证 bench 表格正则与 outputFile 报告格式——三者都有测试兜底(runner-security 集成用例 + check:lessons 课程全量实测),这是升级安全网的实际存在形式;monaco 深路径与 happy-dom DOM 行为属于"升级后按门禁结果处理"的中风险项。废弃与停滞风险(优):无停滞依赖(Enzyme 类案例为零),@testing-library、happy-dom、vite、vitest 全部活跃维护;模板时代遗留物(tests/mocks 死代码等)已在《13》立项清理,不在依赖清单内。安全面(良):依赖无已知高危用途(无 eval 类库、无网络层库);legacy-peer-deps=true 是唯一宽松项,长期建议收紧以暴露真实 peer 冲突。总体结论:这是一份与"做窄做深"架构决策高度一致的依赖清单——数量克制、核心钉死、升级有门禁护栏,健康度在同类个人项目中属上游;需要处理的只有 @types/node 对齐、npmrc 收紧、check-lessons 的 esbuild 隐式依赖三个小项,均已在文中标注。