项目:nas-console | 版本:v1.2 | 生成日期:2026-10-06 | 依据:源码分析
本篇把两个 package.json(前端根目录与 server/)声明的全部依赖逐个过一遍:每个依赖「干什么用、在哪些文件被消费、为什么选它而不是替代品、升级时会踩什么」。00 篇第 3 节给过技术栈速览,06 篇第 1 节给过依赖规模的规模感(前端 7 个运行时、server 2 个),本篇是逐项展开的完整版。盘点口径有三条,先说清楚以避免误读。
第一,版本有两层:package.json 声明的是语义化范围(如 ^6.0.0),实际安装版本以 package-lock.json 锁定为准(如 6.6.5)。本篇两者都列——声明范围反映作者的升级意愿,锁定版本反映「当下真正在跑的东西」,两者差异本身就是信息(例如 react 声明 ^19.1.1 实装 19.3.0,说明依赖安装后有过一次范围内的更新而 lock 被如实提交)。第二,只盘点直接依赖:传递依赖(antd 拉进来的二十余个 @rc-component/* 包、recharts 拉进来的 redux 栈、better-sqlite3 拉进来的 prebuild-install 链)在「重点依赖展开」小节里以依赖树视角讨论,不做逐个表格。第三,选型理由一律溯源:能从源码注释、设计稿或 README 引证的引证原文,引证不到的以「推断」标注,不替作者编造理由。
先给一张全景计数表,建立体感:
| 工程 | 运行时依赖 | 开发依赖 | 依赖树规模(lock 顶层条目) | 说明 |
|---|---|---|---|---|
前端(根 package.json,nas-console@0.3.0) | 7 | 5 | 约 70 个(含传递) | UI 栈重量主要来自 antd 与 recharts |
服务端(server/package.json,nas-console-probe@0.3.0) | 2 | 3 | 约 40 个(几乎全部属于 better-sqlite3 的安装链) | 零框架决策的直接红利 |
两个工程的 dependencies 数量之比是 7:2,而这个 2 里还有一个是日期库——「零框架」决策(见《16-决策记录ADR》ADR-003)在依赖清单上的投影就是这行数字。
| 依赖 | 声明版本 | 锁定版本 | 用途 | 主要使用位置 |
|---|---|---|---|---|
| react | ^19.1.1 | 19.3.0 | UI 框架 | 全部 src/ |
| react-dom | ^19.1.1 | 19.3.0 | React 渲染器 | src/main.tsx(唯一入口) |
| antd | ^6.0.0 | 6.6.5 | 组件库 | 管理区全部页面与表单、公开页局部(Tag/Tooltip/Progress) |
| react-router-dom | ^7.14.2 | 7.18.3 | 路由 | src/App.tsx(BrowserRouter + 路由表 + RequireAuth) |
| recharts | ^3.0.0 | 3.10.1 | 图表底层 | src/charts/ 四个封装组件(Area/Bar/Line/Donut) |
| react-icons | ^5.4.0 | 5.7.0 | 图标集 | src/components/ServiceIcon.tsx(14 个 lu 系列图标映射服务类型) |
| dayjs | ^1.11.11 | 1.11.23 | 日期处理 | 前端按天分桶、时间格式化;经 antd 传递依赖双保险存在 |
React 19 在这个项目里不是「追新」,而是被 antd v6 反向要求的选择——00 篇第 3 节明确「antd v6 原生支持 React 19」。配套证据有两处:vite.config.ts 的 resolve.dedupe: ['react', 'react-dom'],防止依赖树里出现第二份 React 实例(React 的经典炸裂方式是 hooks 报「invalid hook call」,而双实例是最常见诱因);package.json 的 type: "module" 与 Vite 5 的 ESM 管线。替代方案方面,React 18.x 技术上仍可与 antd v6 共存(antd 对 18 有兼容层),但会停留在「被支持但不推荐」区间;Preact/Solid 等替代渲染器与 antd 生态不兼容,从未进入候选。升级风险:React 19 移除了部分废弃 API(string refs、legacy context 等),本项目代码量小且没用这些特性,主要风险点反而是第三方库对 19 的适配——recharts 3.x 与 react-router 7 均已适配,风险集中度低。值得记录的一个事实:react-dom 在源码里只被 main.tsx 的 createRoot 触碰一次,但它承载的调度器(scheduler 包随 lock 安装)是全前端的运行时地基。
antd 是前端最重的依赖(lock 树里二十余个 @rc-component/* 包 + @ant-design/cssinjs + @ant-design/icons),也是本项目历史上一次主动迁移的目的地:src/components/Field.tsx 与 src/theme/feedback.tsx 的注释(「替代 Mantine 风格输入组件」「等价原 notifications/modals 包」)还原了 Mantine → antd v6 的整体迁移史(00 篇第 3 节、02 篇第 5 节均有记述)。迁移是彻底的——新代码直接用 antd 原生 API,兼容桥只保留两个薄文件。
选型理由可从代码反推三点:其一,企业级组件密度,服务管理页的全受控 Table(筛选/排序/分页四件 useState)、表单、Drawer、Modal、Segmented 都是现成品,自研这些组件的成本远超引入成本;其二,深浅色算法内建,ConfigProvider 的 theme algorithm 切换配 #4c6ef5 主色即完成全站换肤(src/App.tsx ThemedApp);其三,zhCN 本地化内建,与项目的中文界面定位吻合。替代方案:Mantine 是被替换的前任(迁移动机从注释无法完全还原,推断与 v6 对 React 19 的原生支持及组件覆盖面有关);shadcn/ui 类无样式方案需要自建大量交互细节,与「个人项目要省维护时间」取向不符。升级风险有三:v6 把 rc-* 包族改名为 @rc-component/*(lock 可证),大版本间内部依赖结构变动剧烈,锁文件必须如实提交;cssinjs 运行时样式在低端 NAS 浏览器上有轻微首帧成本(推断,未实测);项目只用了 antd 表面 API,升级主要风险是 form/table 受控行为的细节变化。
recharts 是本项目一个值得专门揭开的依赖——lock 文件显示它带来了 @reduxjs/toolkit、react-redux 9、redux 5、redux-thunk、reselect、immer(反向追踪可见这些包的直接消费方是 recharts@3.10.1 与 @reduxjs/toolkit@2.12.0)。这是 recharts 3.0 用 redux 重写内部状态管理的结果。对本项目的实际影响有两面:体积与心智两面都几乎为零(项目从不直接触碰这些包),但「依赖树里有一个完整的 redux 栈」这件事应当被接手者知道——否则在未来某次排查「为什么 node_modules 里 redux」时会浪费时间。
项目对 recharts 的消费方式也很有特色:只通过 src/charts/ 封装层使用(Area/Bar/Line/Donut 四个 recharts 组件 + 手写 SVG 的 Sparkline,后者零 recharts 依赖),配色统一走 chart-theme.ts 的扁平色板。选型理由:图表需求只有五种,ECharts 功能过剩且体积大一个数量级;recharts 的声明式 API 与 React 组件模型契合。升级风险:recharts 3.x 相对 2.x 是重写级变更(内部 redux 化),若升级到 4.x 需重点回归 src/charts/ 四个封装组件——好在消费点收敛,回归面可控;封装层的存在正是为了把这类风险锁在一层里。
react-router-dom 7.18.3:消费点同样收敛——src/App.tsx 一个文件定义全部路由与 RequireAuth 门。v7 的 BrowserRouter/Routes/useNavigate API 与 v6 兼容,从 lock 看声明 ^7.14.2 实装 7.18.3 属于范围内自动更新。风险点:v7 开始官方推动 framework mode(loader/action),本项目用的是纯 declarative mode,升级时只需守住这个用法不迁移即可。替代方案:TanStack Router 是更强的类型化路由,但本项目只有 8 条规则加 1 条兜底,类型化路由的收益用不上。
react-icons 5.7.0:真实消费只有 ServiceIcon.tsx 一个文件、14 个 lu(Lucide)系列图标。这是典型的「树摇友好」依赖——import 具名图标时打包器只带入用到的 SVG 路径数据,但 lock 里全量包体在磁盘上约占数十 MB(开发机磁盘成本,不影响产物)。替代方案:直接手写 14 个 SVG 或用 antd 自带 icons 可再省一个依赖,属于「可做但收益极小」的项。
dayjs 1.11.23:全项目唯一「前后端同构」的第三方依赖(前端与 server 各装一份),用途是按天聚合分桶(dayjs(ts).format('YYYY-MM-DD') 是可用率条数据的地基)与日期格式化。选型理由:零配置、immutable、体积极小;替代方案:原生 Intl.DateTimeFormat/Temporal 可覆盖大部分需求,但 Temporal 在 Node 22 尚未稳定(⚠️ 以当前 Node 22 LTS 的 API 状态为准),dayjs 是稳妥解。升级风险近乎为零——它稳定在 1.x 已多年,且两个工程各自锁定互不干扰。
| 依赖 | 声明版本 | 锁定版本 | 用途 | 备注 |
|---|---|---|---|---|
| typescript | 5.5.2 | 5.5.2 | 类型检查(build 前置门)与编译 | 精确锁定无 ^,前后端一致 |
| vite | ^5.4.0 | 5.4.21 | 开发服务器与打包 | 自带 esbuild/rollup/postcss 三个传递重头 |
| @vitejs/plugin-react | ^4.3.1 | 4.7.0 | React Fast Refresh 与 JSX 转换 | 仅 dev 使用 |
| @types/node | ^20.14.9 | 20.14.9 | 前端侧 Node 类型(vite 配置等) | 与 server 侧的 ^22 双轨并存 |
| @types/react / @types/react-dom | ^19.1.1 | 19.3.0 / 19.3.0 | React 类型 | 跟随 react 实装版本 |
三点值得展开。其一,typescript 是全项目唯一精确锁定的依赖("5.5.2" 无前缀,前端与 server 两个工程完全一致)——类型检查是本项目唯一的自动化关卡(09 篇第 2.1 节),检查器本身版本漂移会导致「同一份代码昨天过今天不过」,钉死是合理的防御。代价是错过 5.6+ 的新特性,属于可接受取舍。其二,@types/node 的双轨:前端声明 ^20.14.9(tsconfig target ES2020),server 声明 ^22.15.0(实装 22.20.2,tsconfig target ES2022 且实跑在 node:22 容器)——server 侧的 @types 与运行时对齐是正确姿势,前端侧偏低一档只影响 vite.config 的类型提示,无实际风险。其三,构建工具链的传递重量:vite 5.4.21 拉入 esbuild 0.21.5(二进制)、rollup 4.63.2、postcss 8.5.28——这三个包决定了「换 vite 大版本」是 devDependencies 里最大的升级动作(vite 6/7 已发布,升级需回归 dev 代理行为,尤其 /sse 的 x-accel-buffering 注入逻辑,见 06 篇第 2 节)。
| 依赖 | 声明版本 | 锁定版本 | 用途 | 主要使用位置 |
|---|---|---|---|---|
| better-sqlite3 | ^11.10.0 | 11.10.0 | SQLite 同步驱动,唯一存储 | server/src/store.ts(唯一 import 点) |
| dayjs | ^1.11.11 | 1.11.23 | 日期分桶(daily 表的 day 键生成) | store.ts(record/dailyBuckets/prune 三处) |
| @types/better-sqlite3(dev) | ^7.6.13 | 7.6.13 | 驱动类型 | — |
| @types/node(dev) | ^22.15.0 | 22.20.2 | Node 类型(与运行时 22 对齐) | — |
| typescript(dev) | 5.5.2 | 5.5.2 | tsc 编译到 server/dist | build/dev 脚本 |
better-sqlite3 是 server 唯一的「带原生代码」依赖,也是整个项目依赖管理复杂度的集中点。它的选型理由在源码里写得直白(store.ts 头注释):「WAL 模式 + 同步 better-sqlite3 API,单实例个人规模绰绰有余」——同步 API 在 0.6 行/秒的写入速率下没有事件循环阻塞风险,却省掉了异步驱动的回调/编排层,代码可以写成朴素的顺序语句(record() 全文没有一个 await)。
真正需要接手者理解的是它的安装链。server 的 package-lock 显示 better-sqlite3 拉入约 40 个传递包,几乎全部服务于「把预编译二进制装到位」这一件事:prebuild-install(7.1.2)尝试从 GitHub Releases 下载与 node-abi(3.96.0)算出的 ABI 匹配的 .node 文件;tar-fs/tar-stream/decompress-response 负责解包;下载失败才回退现场编译(需要 python3/make/g++)。这条链解释了三个部署层决策:容器基础镜像必须是 bookworm(glibc) 而非 alpine(musl)(Dockerfile 第 16 行注释:「bookworm(glibc)保证 better-sqlite3 命中预编译二进制;alpine(musl) 需要现场编译」);Dockerfile.nas 的 deps 阶段必须在 NAS 实际平台上执行 npm ci(二进制与平台绑定,14 篇第 3.3 节);离线/弱网环境安装可能退化到编译路径(⚠️ 仓库内无镜像源配置,以部署机网络为准)。
替代方案的评估在设计稿时期已做过:node:sqlite 内置模块在 Node 22 仍是实验性(设计稿写作时更不可用)、纯 JS 的 sql.js 无 WAL 且性能不足、JSON 文件方案在按天聚合场景退化为全文件重写。升级风险:11.x → 12.x 时 Node ABI 支持窗口变化可能要求先升 Node 版本(两者强耦合);预编译产物的平台覆盖以官方 Releases 为准(⚠️ 具体平台清单随版本变动,升级前查 release notes)。风险缓解的事实是:本项目只用了 prepare/run/get/all/exec/pragma/transaction 六七个 API,surface 极小。
server 的 dayjs 与前端同声明 ^1.11.11、同实装 1.11.23,但两个工程各有各的 package-lock——依赖树独立、磁盘上可能存在两份拷贝(pnpm/npm 的提升策略下前端根的 dayjs 不会自动供 server 使用,Dockerfile 也是分开 npm ci 的)。这是刻意的隔离:server 的依赖变更不影响前端构建产物,反之亦然。使用面只有一个文件(store.ts),只用了 dayjs().format()/subtract() 两个 API,替换成本极低。
把两棵依赖树放在一起看,可以画出一张「重量守恒」图:前端的重量在 UI 层(antd 25+ 包族、recharts 8 包族),服务端的重量在存储安装链(better-sqlite3 的 prebuild 机制),除此之外几乎没有第三块。recharts 带来的 redux 栈与 antd 带来的 @rc-component 包族都属于「框架内部复杂度」,项目代码完全不感知;better-sqlite3 的 prebuild 链属于「安装期复杂度」,运行期消失。换句话说,这个项目把依赖复杂度压到了两个「黑盒」里,黑盒之外的业务代码零框架零运行时依赖——这与 00 篇第 3 节「能不引依赖就不引」的表述精确互证。
按任务视角再给一张「谁依赖谁」的速查(只列直接消费文件,完整模块地图见 07 篇第 1 节):
antd → 管理区页面(Dashboard/Analytics/Services/Settings)+ AppLayout + 表单类组件;公开页只用 Tag/Tooltip 等轻组件。recharts → 仅 src/charts/ 四个文件;页面不直接 import recharts。react-router-dom → 仅 src/App.tsx。react-icons → 仅 src/components/ServiceIcon.tsx。dayjs → health-history.ts、Analytics.tsx、chart 日期轴等(前端)+ store.ts(服务端)。better-sqlite3 → 仅 server/src/store.ts。这张表的工程含义:每个重依赖的消费点都是单点,替换或升级的影响面可以用 grep 精确圈定。这是 11 篇「分层不越级」总则在依赖管理上的投影。
综合声明版本、锁定版本、使用面与升级路径,本项目的依赖健康度评估如下(分五档:优/良/中/关注/警示)。
良——核心运行时(react 19.3.0、antd 6.6.5、better-sqlite3 11.10.0):三者都在各自的大版本前沿或接近前沿,使用面收敛,官方维护活跃。antd 声明 ^6.0.0 实装 6.6.5 说明范围内更新在正常发生。
良——构建链(vite 5.4.21、typescript 5.5.2 钉死、@vitejs/plugin-react 4.7.0):typescript 钉死是正确的;vite 5 仍在维护窗口内但已非最新大版本,升级到 vite 6/7 的主要回归点是 dev 代理(尤其 /sse 注入头)与 Node 版本要求,建议在有空窗期时做,非紧迫。
中——双 lock 双 dayjs、@types/node 双轨:结构合理但存在「两处升级同一依赖」的心智成本;无实际风险。
关注——三类已登记项:①Dockerfile 的 COPY react-ui/ 与 compose.yml 的 context: ..——当前依赖清单中已无 react-ui(dependencies 与 lock 均无),该 COPY 是历史遗留(13 篇问题三已展开),它使全量构建凭空多出一个仓库前置条件,是依赖清单与构建脚本之间唯一的「说谎点」;②package.json 描述(「服务健康监控 + Umami 访问分析」)与实际功能面的漂移(00 篇第 6 节);③无测试运行器(09 篇核心议题),devDependencies 里没有 vitest/jest,tsconfig 预留的 exclude 尚未兑现。
警示——无。没有发现过期到有已知漏洞的依赖(⚠️ 本评估基于版本事实与维护状态推断,未做 npm audit 实测,建议接手后跑一次并记录结果)。
给接手者的三条依赖纪律,作为本篇收尾。第一,加依赖前先过「量级算术」(16 篇元原则一):这个项目的既有决策说明,服务数 19/用户 1 的量级下,绝大多数「标准配置」都是过度设计;新依赖必须回答「现有 9 个依赖为什么不够」。第二,升级顺序先 server 后前端:server 依赖只有两个且 better-sqlite3 与 Node ABI 强耦合,升级前先确认目标版本对 Node 22 的支持窗口,再动前端(前端依赖树大但消费点收敛,回归面是 charts/ 四文件与管理区页面)。第三,lock 文件与 package.json 同等对待:本项目两次范围内更新(react 19.1.1→19.3.0、react-router 7.14→7.18)都被如实锁进 lock,这个纪律值得延续——对依赖树里藏着 redux 栈这类「惊喜」的项目,lock 是唯一的事实记录。
各依赖对应的模块消费细节见 07-模块详解;better-sqlite3 的存储设计与 WAL 语义见 04-数据模型第 2 节与 10-性能第 1 节;react-ui COPY 遗留的完整处置建议见 13-技术债务问题三。