项目:react-ui | 版本:v1.2 | 生成日期:2026-10-06 | 依据:源码分析
依赖是一个组件库最诚实的「资产负债表」:package.json 里的每一个条目都对应一份维护承诺与一次下游升级风险。本篇把 monorepo 四层依赖面逐个走完——根 package.json(工程底座)、packages/hooks(应然的最小面)、packages/ui(真正的运行时主面)、packages/@xiaoye-react/pro(刻意的零依赖)与 apps/docs(文档站生态)——每条给出版本、用途、使用位置、选型理由、替代方案与升级风险。结尾单列 patches/ 补丁依赖并给出整体健康度评估。选型理由一律以源码注释、README、ADMIN-ROADMAP 或包描述为据;属本篇分析的推断处会显式说明。机制与版本发布面的既有结论(外部化策略、peer 下限案例、发布顺序)以「见《0X-xxx》」引用,不再展开。
一个先给的结论:这张表总体是「小而克制」的——三个发布包的运行时依赖合计 20 条且全部有明确消费点(除一条例外,见第 9 节 klona 条目),没有任何「顺手装上」的全家桶式依赖。
根包 react-ui@1.0.0 是 private: true 的 workspace 枢纽,devDependencies 全部服务于质量门禁与构建链,无一条进入发布产物:
| 依赖 | 版本 | 用途 | 使用位置 | 选型理由 | 替代方案 | 升级风险 |
|---|---|---|---|---|---|---|
| pnpm(packageManager) | 9.12.3+sha512 | 包管理与 workspace 协议 | 根 package.json 的 packageManager 字段 | 版本+哈希锁定防供应链漂移;workspace:*/workspace:^ 协议是发布流程的根基 | npm/yarn workspaces | 大版本升级可能改变 lockfile 格式与 publish 行为(workspace: 重写语义) |
| typescript | 5.5.2(三处一致) | 类型系统 | 根/三包 devDeps | 版本在四个 package.json 中手工对齐,避免类型推断漂移 | — | 升级需过 pnpm typecheck 三套配置 + check:published-types |
| eslint / typescript-eslint / @eslint/js | 9.5.0 / 8.63.0 / 9.5.0 | flat config lint | eslint.config.mjs | flat config 是 ESLint 9 正道;棘轮挂 --max-warnings=57 | — | 类型安全规则受 OOM 限制(见《13-技术债务》D6) |
| eslint-plugin-react-hooks | ^5 | hooks 规则(exhaustive-deps 台账) | eslint.config.mjs | 棘轮治理的另一半 | — | 升级可能新增规则导致棘轮回涨,需先豁免再消化 |
| stylelint + config-standard | 16.6.1 / 36.0.1 | CSS 规范 | stylelint.config.mjs | 与 postcss-nested 语法兼容 | — | 低 |
| postcss-nested | ^7.0.1 | CSS Modules 内嵌套语法 | 根 postcss.config.cjs、ui devDeps | 组件 module.css 用嵌套写法(见各 *.module.css) | postcss-nesting(规范版) | 语法兼容性低风险 |
| postcss-styled-syntax | 0.6.4 | stylelint 对模板字符串样式的解析 | stylelint 链 | 为 Emotion 通道的样式检查兜底 | — | 低 |
| cspell | 8.9.1 | 拼写门禁 | cspell.json、pnpm spellcheck | 防 typo 入库(含中文项目词典) | — | 词典格式变更需迁移 |
| commitlint + config-conventional + commitizen + cz-git | 19.3.0 / 19.2.2 / 4.3.0 / 1.9.3 | 提交规范 | commitlint.config.cjs、pnpm commit | conventional 提交 + 交互式生成 | — | 低(commitlint.config.cjs 尾部约 80 行注释掉的汉化死配置,见《13》D10) |
| prettier | 3.3.2 | 格式化 | .prettierrc、docs 依赖同款 | 单一格式化事实源 | — | 低 |
| playwright / @playwright/test | ^1.61.1 | E2E 双配置 | 根两份 playwright 配置 | 截图 10 条 + 交互 24 条两套跑法(见第 7 节深读) | Cypress | 浏览器内核升级牵动截图基线(跨平台不通用,见《13》D7) |
| react / react-dom(devDeps) | 19.1.1 | 开发期宿主 | 根 devDeps;经 overrides 钉 19.2.7 | overrides 保证全仓单实例(见 ADR-002,《16-决策记录》) | — | 与 overrides 的双版本并存需留意同步 |
| @types/node / @types/react / @types/react-dom | 20.14.9 / 19.2.3 / 19.2.3 | 类型声明 | 根 devDeps + overrides | @types/react 与 React 行为对齐 | — | 升 React 必须同步升 @types |
根依赖里最值得注意的一条纪律是:TypeScript 版本四手对齐(根、hooks、ui、pro 的 devDeps 都是 5.5.2)——组件库的类型产物对 TS 编译器版本敏感(vite-plugin-dts 的发射与 check:published-types 的复检都吃这个版本),版本错位会产生「本机能过、发布产物类型报错」的幽灵问题。
packages/hooks/package.json 没有任何 dependencies 字段——83 个 hook 全部是纯 React 原语。devDeps 只有测试与构建两族(vitest 1.6 系、testing-library 系、jsdom、vite 5.4 系、vite-plugin-dts 4.3 系、typescript 5.5.2),peer 仅 react/react-dom(^18.0.0 || ^19.0.0)加 sideEffects: false。这是全仓健康度最高的一张依赖表:运行时依赖为零意味着它对任何宿主项目都是零成本的,也正是 ADR-001(《16-决策记录》)能成立的物质基础。升级风险面几乎不存在——唯一要守的是「不引入任何带副作用的模块」,否则 sideEffects: false 的承诺变成谎言,下游摇树会删掉真实需要的代码。
| 依赖 | 版本 | 用途 | 使用位置 | 选型理由 | 替代方案 | 升级风险 |
|---|---|---|---|---|---|---|
| @floating-ui/react | ^0.27.19 | 浮层定位引擎 | Tooltip/Popover/HoverCard/Menu/Combobox/Spotlight 的 use-* 与 Combobox.tsx | 中间件式定位(翻转/防溢出/箭头)、无障碍交互内建 | popper 旧版(已并入 floating-ui)、自研定位 | 0.x 大版本 API 不稳;升级需过浮层族全量单测与 e2e |
| @tanstack/react-virtual | ^3.13.0 | 虚拟滚动 | DataTable.tsx(useVirtualizer 始终调用、measureElement 引用直传) | headless、与滚动容器解耦、虚拟列测量成熟 | react-virtualized(重)、自研 | v3 内部 API(measure/resize 语义)变更会牵动展开行测量(见《05》主题二) |
| clsx | ^2.1.1 | 类名拼接 | 全库 className 合成底层 | 2KB 内、无依赖、条件语法成熟 | classnames、自写 join | 几乎无 |
| embla-carousel-react | ^8.6.0 | 轮播内核 | components/Carousel/Carousel.tsx;内部包 @xiaoye-react/carousel 复用 | 轻量、插件化(Autoplay 等)、无样式侵入 | swiper(重、样式耦合) | 8.x 稳定;插件 API 变更影响 story 与文档 demo |
| react-number-format | ^5.4.5 | 格式化数字渲染 | NumberFormatter.tsx | 处理千分位/前后缀/本地化格式的边角逻辑远超自写成本 | 手写 Intl.NumberFormat 封装 | 需关注 SSR/受控语义版本差异 |
| react-remove-scroll | ^2.7.2 | 滚动锁定 | ModalBase.tsx、AppShell.tsx;index.ts 再导出供消费者复用 | 处理滚动锁定的全部边角(滚动条补偿、嵌套锁、touch) | body overflow 手术(有布局抖动问题) | 与 React 19 兼容性已验证;升级低风险 |
| dayjs | >=1.0.0 | 日期运算内核 | dates/utils 与 33 个日期组件 | 体积小、插件化、API 与 moment 亲和 | date-fns(函数式)、luxon | 范围声明宽松(>=1.0.0),实质依赖插件生态稳定;全局状态(extend/locale)要求单实例(vite.config 外部化注释) |
| rrule | ^2.8.1 | 重复事件规则(RFC 5545) | schedule/utils/expand-recurring-events | 日程重复语义自研不现实 | rruler、自写子集 | 双入口包(README「发布相关的坑」第 3 条:CJS/ESM 依赖需命名空间优先取值 + 计算键 default);升级必须过消费者侧 ESM 复验 |
| @standard-schema/spec | ^1.1.0 | 校验库统一接口 | form/schema-resolver.ts 及其测试 | 一个接口解耦 zod/valibot/自研校验器 | 绑定 zod(会失去适配层) | spec 处于 v1 稳定期;低 |
| fast-deep-equal | ^3.1.3 | 深比较 | form/hooks/use-form-status | 表单 dirty 判定的热路径,性能优于手写递归 | lodash isEqual(重) | 几乎无 |
| klona | ^2.0.6 | 深克隆 | 已无消费点(死依赖) | 历史上用于 form/paths 的整表深克隆 | 已被 copy-on-write 浅克隆替代(见第 9 节) | 建议移除 |
| csstype | ^3.2.3 | CSS 类型定义 | emotion/types.ts | TS 官方 CSS 类型,零运行时 | 手写 style 类型 | 随 TS 生态自动演进,低 |
| html-react-parser | ^5.2.17 | HTML→React 转换 | emotion/ssr.tsx(服务端关键样式抽取) | Emotion SSR 输出是 HTML 字符串,需转 React 节点 | 手写解析(不值得) | 仅在 emotion 可选通道被加载;主路径无影响 |
| chroma-js | >=2.4.2 | 色板生成 | colors-generator/generate-colors.ts(附 chroma-js.d.ts 补声明) | 从主色生成 10 阶色板是色彩学问题,自研不划算 | culori | 范围宽松;d.ts 是本地 shim,上游出类型后可删 |
| @emotion/cache、@emotion/react、@emotion/serialize、@emotion/utils | ^11.14.0 / ^11.11.4 / ^1.1.4 / ^1.2.1 | 可选 CSS-in-JS 通道 | src/emotion/ 全目录(见第 7.3 节) | 见第 7.3 节 | styled-components、linaria | 四包需同步升级(内部互相依赖序列化格式);@emotion/cache 插入点是全局状态,双实例即 bug(vite.config 注释) |
| type-fest | 4.41.0(精确锁) | 类型工具集 | core/UIProvider/theme.types.ts、Transition/get-transition-props | 主题键位推导用 ConditionalExcept 等高阶类型 | 自写类型工具 | 精确版本锁定值得注意:type-fest 各版本类型定义不保证向后兼容,锁死避免推断漂移 |
ui 的 devDeps 里混着三类东西:测试资产(vitest 1.6 系、@testing-library 三件套、jsdom)、构建资产(vite 5.4 系、vite-plugin-dts 4.3 系、@vitejs/plugin-react、postcss-nested)、以及三个值得单独说明的条目——@xiaoye-react/hooks 与 @xiaoye-react/tests(workspace:* 引用内部资产,前者同时是 peer,开发时经 devDeps 短路到源码仓)、zod ^4.4.3(schema-resolver 的测试演示依赖,恰好在 devDeps 而非 dependencies,说明「校验库永远属于消费者」的立场)、@phosphor-icons/react 与 lodash(story 与测试用,不进产物)。vite-plugin-dts 的两个行为补丁(afterDiagnostic 强制失败、beforeWriteFile 修 __ 前缀重命名 bug)见《14-配置与环境详解》第 3 节,是 devDeps 中行为风险最高的一个——它的上游 bug 修复节奏直接决定 ui 的 dts 发射质量。
pro 包的 package.json 没有依赖条目——三个页面模式(PageContainer/SearchFilter/ProTable)全部由 peer(ui、hooks、react/react-dom)组装而成,连工具函数都没有自带。这是 ADMIN-ROADMAP「总体策略:分层放置,复用优先,不重写已有组件」的依赖面表达:模式层若有自己的运行时依赖,就会在消费者应用里出现「pro 的 dayjs 与 ui 的 dayjs」这类双实例问题。风险点同样清晰:pro 对 ui 的耦合是深度的(ProTable 直接内嵌 DataTable + SearchFilter),ui 的大版本升级天然要求 pro 跟进,peer 的 workspace:^ 在发布时重写为 ^3.0.1 一类的区间,区间内 ui 的破坏性 minor(若发生)不会被 peer 拦截——依赖 pro 的项目应把 ui 与 pro 视为一个联动升级单元(见《03-接口设计》第 7 节)。
文档站依赖面显著宽于任何发布包(约 40 条),可归五族:dumi 内核族(dumi ^2.4.30、dumi-plugin-color-chunk,构建链绑定 webpack/mako,patch 见第 8 节);演示与代码展示族(@codesandbox/sandpack-react、@stackblitz/sdk、prismjs、highlight.js、vanilla-jsoneditor、mermaid);交互演示素材族(@dnd-kit 三件套、motion、react-virtuoso、embla-carousel-autoplay、swr);图标族(@phosphor-icons/react、react-icons——正是启动性能优化中「barrel 退出编译图」事件的当事依赖,438 文件改写、入口包 20.6→13.7MB,见 ADMIN-ROADMAP 进度节);工程脚本族(ts-morph 驱动 docgen、tsx、glob、semver)。文档站依赖不需要发布面的严苛纪律,但有两点仍然要紧:其一,@xiaoye-react/ui|hooks|pro 走 workspace:*,本地源码即所见;其二,文档站是「文档即测试」机制的载体(docgen 与 docs:lint 三脚本),所以 ts-morph 的版本升级会直接影响 API 表核对能力。
全库浮层(Tooltip/Popover/HoverCard/Menu/Spotlight/Combobox 及其上的 Select 家族、日期输入族)统一构建在 @floating-ui/react 之上,没有第二个定位实现。这个「单引擎」决策的收益在《02-组件设计》第 2.5/2.9 节已经展开(浮层基建复用图);从依赖视角补充两点:其一,floating-ui 的中间件配置(flip/shift/arrow/size)在各组件的 use-tooltip/use-popover 等文件中各自组装,依赖升级时这些是回归排查点;其二,floating-ui/react 0.x 系列版本号意味着「次版本也可能带行为修正」,锁 ^0.27.19 是当前测试面(浮层族全量单测 + modal/slider 等 e2e)能兜住的范围,升 1.x 应视为结构演进立项(按《13-技术债务》第三阶段的立项标准走)。
这是全库与业务代码咬合最深的一条依赖:DataTable 的虚拟行渲染、measureElement 引用直传、展开行双行高度合成上报(resizeItem),全部使用 tanstack v3 的内部语义(见《05-核心实现流程》主题二)。ADMIN-ROADMAP 决策 2(2026-09-09)明确拍板「@tanstack/react-virtual 进 ui 包 dependencies,DataTable 直接内置虚拟滚动」,理由是「demo 已验证过,先例充分」。升级风险因此不是「能不能跑」而是「测量语义会不会变」——v3 的动态测量与 resize 观测行为若有调整,展开行高度修正、固定列对齐这类精细逻辑需要重验。仓储里 apps/docs 也依赖 react-virtuoso 做文档站自身的长列表,但那是文档站自用,与组件库依赖面无关,不能混为一谈。
Emotion 在 ui 的 dependencies 里占五席(cache/react/serialize/utils 四个运行时包,加上根部的 postcss-styled-syntax 协助 lint),但主路径(CSS Modules)一个都不 import——它们只被 src/emotion/ 的可选通道消费:UIEmotionProvider + create-styles + emotion-transform(以 stylesTransform 注入 UIProvider)+ use-css/merge-sx + ssr.tsx(html-react-parser 抽取服务端关键样式)。这个依赖形态值得学习:「可选能力」以 dependencies 而非 peerDependencies 存在——消费者不装任何东西也能用主路径,装了 ui 就天然具备 emotion 通道,代价是所有消费者都背着这四个包的安装体积(虽然不 import 就不进产物)。设计谱系上,这条通道与 Mantine v6→v7 的演进轨迹同构(v6 的 create-styles 基于 emotion,v7 转向 CSS Modules + CSS 变量并保留兼容层)——本库内核(factory/StylesApi/varsResolver/useProps/CSS 变量主题)与 Mantine 的同构性是结构事实,但仓库内没有任何文字声明这一谱系 ⚠️ 待确认(作者意图层面)。从依赖治理看,若未来确认 emotion 通道无真实消费者,五包合计是最大的一条可裁剪面。
dates 模块 33 个组件全部基于 dayjs(dates/utils/ 封装层),schedule 模块的重复事件基于 rrule(schedule/utils/expand-recurring-events/)。两条依赖的性格完全不同:dayjs 是「基础设施型」——版本范围声明为 >=1.0.0(极宽),真正的约束不在版本号而在单实例纪律(插件 extend 与 locale 注册是全局副作用,这正是 vite.config 外部化注释点名它的原因);rrule 是「功能型」——^2.8.1 锁区间,但它是 README 发布坑清单里唯一的第三方包案例(CJS/ESM 双入口,main 与 module 指向不同构建,消费代码要「命名空间优先、计算键 default」),这意味着 rrule 的每一次升级都必须走消费者侧 ESM 复验,不能只看单测绿。
根 playwright.config.ts(截图 e2e,托管 apps/docs/dist,10 条基线)与 playwright.interactions.config.ts(交互 e2e,Vite harness 端口 4175,24 条用例)共用 playwright ^1.61.1 与 @playwright/test ^1.61.1 双 devDeps。选型理由在《09-测试与质量保障》第 3 节已展开;依赖视角的风险集中在截图基线:内核渲染随版本漂移会让基线批量失真,而基线「跨平台不通用」导致 CI 不跑截图(D7)——升级 playwright 版本时,截图套件实际上是在本地以「人工对差异」方式复验的,这是当前依赖面里最依赖人的一个环节。
文档站选择 dumi 2 的理由(markdown 长文 + demo + API 表三合一、可被脚本核对)见《00-项目总览》第 9 节;代价是绑定 webpack/mako 构建链(ADMIN-ROADMAP 实测:mako 无持久化缓存、webpack fs cache 冷 21min/热 146s 均不划算,保留 mako 并把图标 barrel 移出编译图)。dumi 在 pnpm.overrides.patchedDependencies 里被登记为 dumi@2.4.47: patches/dumi@2.4.47.patch——补丁内容见第 8 节。升级风险:dumi 2.x 内部(dist 产物)被直接 patch,任何上游升级都会使补丁失配,需要重新定位对应产物并重做 diff;这也是 ADMIN-ROADMAP 把「迁移 rspress/vitepress」列为长期可选项的根本原因——框架级依赖的不可控性只能用「可迁移性」对冲。
patches/ 目录当前只有一个补丁:patches/dumi@2.4.47.patch(19 行),在根 package.json 的 pnpm.patchedDependencies 登记。补丁内容是删除 dumi 产物中 DumiDemo 组件的 React.memo 包装——原实现用「JSON.stringify 前后长度相等」作为 props 判等,补丁注释记载了故障与根因:「demo 数据异步就位后,Suspense 重试渲染会被该 memo 的 props 判等命中而 bailout,骨架屏永久停留(schedule 视图页 100% 复现)」。
这个补丁是全仓依赖治理的一个标本,有三条纪律值得记录:其一,补丁必须带故障复现描述(「schedule 视图页 100% 复现」让未来的维护者能验证补丁是否仍必要);其二,补丁对象是构建产物而非源码(diff 的目标文件是 dist/client/theme-api/DumiDemo/index.js),因此 dumi 小版本升级即失配——依赖声明里的 ^2.4.30 区间实际上被 patchedDependencies 收窄为「2.4.47 可用」;其三,补丁是债务不是资产,第 9 节的健康度评估将其计为维护面而非功能面。《08-安全与权限设计》第 2.2 节已从供应链视角讨论过 patches 的作用与纪律,本节不重复。
综合四层依赖面,给出六个维度的评估:
1. 依赖数量与密度:优。 三个发布包运行时依赖合计 20 条(ui 20、hooks 0、pro 0),无传递性大件(无 UI 框架、无状态管理库、无路由),除 klona 一条死依赖(见下条)外每条都有可指认的消费文件。对照同类组件库(普遍 30-60 条),这是刻意的「最小面」结果(ADMIN-ROADMAP 总体策略、《08-安全与权限设计》第 2.3 节「运行时依赖的最小面」)。
2. 死依赖:一处。 klona ^2.0.6 声明于 ui 的 dependencies,但全 src 检索无任何 import——唯一的痕迹是 form/paths/set-path.ts 第 3-4 行注释:「此前每次 setPath 都对整表值做 O(整表) 深克隆(klona),大表单高频键入时是稳定热点;实际上只有路径上的祖先链需要克隆」——即 klona 已被 copy-on-write 浅克隆优化取代,依赖声明却留了下来。这是本篇回源码的新发现,建议在下一批卫生清理(《13-技术债务》第一阶段)中移除并过一次全量单测。
3. 版本声明纪律:良,有两类例外。 绝大多数依赖用 ^ 区间,符合生态惯例;例外是 type-fest 4.41.0 与 react-dropzone 15.0.0 的精确锁定(前者是类型库版本敏感的合理防御,后者精确锁定的理由仓库内未见说明 ⚠️ 待确认),以及 dayjs >=1.0.0、chroma-js >=2.4.2 的下限式宽松声明(实质靠 lockfile 与单实例纪律兜底)。TypeScript 四处 5.5.2 对齐是这套纪律里执行得最好的一条。
4. 补丁面:小而可控。 仅 1 个补丁、19 行、带复现描述,且登记在 pnpm.patchedDependencies 由 pnpm 统一应用——没有散落的 postinstall 改写。维护成本集中在 dumi 升级时重建补丁。
5. 双入口与互操作风险:聚焦于一点。 全仓唯一已知的 CJS/ESM 双入口第三方依赖是 rrule,且已有成文的消费模式与消费者侧复验流程兜底(README 第 4 步 + release.mjs 显式执行)。这个风险模型是健康的:风险被识别、被文档化、被流程化。
6. 升级风险的分布:冷热分明。 热区三处——@floating-ui/react(0.x、浮层族全覆盖)、@tanstack/react-virtual(与 DataTable 测量语义咬合)、dumi(patch 失配);温区若干(vite-plugin-dts 的上游 bug 行为、playwright 截图基线);其余基本是「升了大概率没事」的冷区。给后续维护者的操作建议:热区升级一律按《13-技术债务》第三阶段的结构演进标准立项(实测数据、替代方案对比、回滚方案),温区升级走正常门禁全绿,冷区随例行手动升级节奏即可(2026-10-06 回刷核实:全仓无 dependabot/renovate 配置,.github/ 下仅 workflows/ci.yml,依赖更新为手动节奏)。
一句话收束:这张依赖表的治理水平高于「单人仓」的平均线——最小面是有意识的结果而非运气不足,唯一的不对称在于:热区风险已有成文流程,而像 klona 这样的「尾巴」靠本轮回源码才现形。建议把「依赖消费点检索」纳入《09-测试与质量保障》式的周期审计清单,让死依赖无处存活。