本节摘要:工作区让一个仓库里的多个包互相引用如引用第三方包一样自然,却无需发布到远端。本节以一个"应用加组件库加工具库"的典型仓库为例,走完顶层声明、内部引用、命令分发、版本发布的完整流程,并给出从单包仓库迁移时的三个高频坑。读完你应能独立搭起一套多包仓库的依赖治理结构。
假设我们要维护一个产品矩阵:一个 Web 应用、一个自研组件库、一个工具函数库。组件库依赖工具库,应用依赖两者。多仓库方案要把"改工具库的一个函数"拆成三个仓库的三次修改加三次发布;单仓库方案把三者放进一个仓库,让包管理器接管内部关系。后者就是 Monorepo,而工作区是包管理器为它提供的官方支持。
搭建从顶层清单开始:
{ "name": "product-matrix", "private": true, "workspaces": [ "apps/*", "packages/*" ], "scripts": { "build": "npm run build --workspaces --if-present" } }
四个要点逐一圈注。private 为真是工作区仓库的标准配置——仓库根永远不会被发布,声明 private 防止误操作。workspaces 字段用通配目录声明子包位置,安装器按 glob 扫描收集子包清单。顶层 scripts 可以借 workspaces 参数分发命令:一条命令广播到全部子包,--if-present 让没有该命令的子包安静跳过。子包清单保持普通形态,只是依赖声明可以指向仓库内部的包名。
子包之间的引用写法与第三方依赖毫无二致。应用包的清单里写:
{ "name": "@matrix/app", "dependencies": { "@matrix/ui": "*", "@matrix/utils": "*", "lodash": "^4.17.21" } }
安装时,安装器识别出 @matrix/ui 是仓库内部包,不搜索远端,而是在 node_modules 里建一个指向仓库内真实目录的软链接。勘验一下:
$ ls -l node_modules/@matrix/ ui -> ../../packages/ui utils -> ../../packages/utils
这条软链接是工作区的全部魔法:代码里 require 内部包名,模块查找算法沿目录找到软链接,软链接跳回仓库源码目录——改了源码立即生效,无需构建发布。跨包重构、联动调试,都是这条链接送的顺水人情。

日常操作的三个高频命令形态:
$ npm run build -w @matrix/ui # 只构建组件库包 $ npm install lodash -w @matrix/app # 给指定子包加装依赖(清单与锁同步更新) $ npm publish --workspaces # 按序发布全部可发布子包
发布是工作区里最需要纪律的环节。内部包互相依赖时,版本号必须协同推进:工具库发新版,组件库清单里对它的声明范围要跟着更新,否则发布出去的组件库在仓库外无法解析依赖。团队规模上来后,这段协同逻辑交给版本管理工具统一裁决(变更日志、版本递进、发布次序一站完成),手写版本号的日子应该尽快结束。
从单包仓库迁入工作区,三个坑按出现频率排序。坑一:幽灵依赖被锁死。 原来靠提升"蹭到"的包,工作区安装后顶层可见性改变,部分隐式引入突然报错——迁移前先按 2.4 节做一遍未声明引入清查。坑二:锁文件合并冲突面变大。 全仓库一把锁,任何子包动依赖都会改锁文件,多人并行时冲突频率上升——3.3 节的"整份重生成"流程照用,但要预想更高的触发频率。坑三:误把内部包发布出去或该发的没发。 内部私有包务必在各自清单里声明 private 或使用组织私有前缀,发布前用模拟发布参数(--dry-run)核对发布名单。
工作区的三条纪律:清单私有位声明到位、内部依赖用组织前缀、发布前模拟核对名单——三条守住,Monorepo 的依赖层就稳了。
其一:两个子包能各用不同版本的同一个第三方依赖吗? 能。工作区不改变提升与嵌套规则——版本不兼容的照旧降级嵌套,各回各家。但要警惕它掩盖的设计问题:两个包对同一依赖的版本撕裂,往往意味着组件库与应用的升级节奏已经脱节,值得在例会上对表,而不是放任双版本长期共存。
其二:顶层和子包都定义了同名脚本,跑哪个? 在仓库根直接跑,命中顶层定义;带包指定参数跑,命中对应子包的定义。约定优于配置:全局任务(全量构建、全量测试)定义在顶层,子包只定义自己的构建——避免同名脚本在两个层级各说各话。
其三:子包互相依赖成环怎么办? 安装器能处理环(软链接不关心先后),但发布与构建顺序会乱。规范做法是把环拆掉:被共享的部分下沉成独立的底层包。依赖环在单包仓库里无感,在工作区里会被发布流程放大——发现环的第一时间就拆,成本最低。
工作区用软链接把布局玩出了新高度,下一节看更激进的玩法:把 node_modules 整个取消掉,用一张查找表管依赖——免安装模式的机制、动机与代价。