Yarn Workspaces共享Lib最佳实践咨询:多项目场景配置需求
嘿,这个场景我之前在团队维护多项目共享库的时候也碰到过,刚好能给你一套落地的解决方案,完美解决「开发时本地联动不用反复更依赖」和「构建/生产时依赖稳定不自动更新」的矛盾。
先拆解你的核心需求
你要的其实是:
- 开发阶段:client/admin能实时用上lib/theme的本地修改,不用手动更新依赖
- 构建/生产阶段:client/admin用lib的正式npm版本,避免本地开发代码混入生产包
- 额外要求:lib作为多外部项目的依赖,版本可控,不会自动更新
一、先搞定「避免lib自动更新」的问题
不管lib在不在workspaces里,核心是锁定依赖版本,而不是盲目移出workspaces:
- 如果lib留在workspaces:在client/admin的
package.json里,把lib的依赖写成固定版本(比如"lib": "1.2.3"),别用^或~前缀,根目录的yarn.lock会帮你锁死具体版本,不会自动拉取更新。 - 要是你想用Git标签控版本:也可以在client/admin里直接指定Git标签作为依赖,比如
"lib": "git+ssh://git@your-repo/lib.git#v1.2.3",这样只会拉取对应标签的代码,完全不会自动更新。不过这种方式开发时联动会麻烦,后面讲怎么解决。
二、开发用本地workspace版本,构建用npm包的最优方案
这才是核心痛点,推荐用Yarn resolutions + 环境变量的组合,不用移出lib,同时满足两种场景:
1. 保持lib在workspaces里(开发便利性拉满)
先别把lib移出workspaces,Yarn Workspaces默认会给本地包创建软链,修改lib代码后client/admin能实时热更新,完全不用手动跑npm update。根目录package.json的workspaces配置保持不变:
{ "workspaces": [ "packages/client", "packages/admin", "packages/theme", "packages/lib" ] }
2. 用resolutions+环境变量切换依赖来源
Yarn的resolutions可以强制指定依赖的版本/来源,我们用环境变量来控制构建时是否用npm正式版:
第一步:根目录package.json加条件性resolutions
{ "resolutions": { "lib": "${LIB_VERSION:-workspace:}" } }
这里的语法是:如果设置了LIB_VERSION环境变量,就用指定的npm版本(比如1.2.3);没设置的话,默认用本地workspace版本。
第二步:修改client/admin的脚本
在client和admin的package.json里,把启动和构建脚本改成:
{ "scripts": { "start": "yarn && react-scripts start", // 开发时默认用本地workspace版本 "build": "LIB_VERSION=1.2.3 yarn && react-scripts build" // 构建时指定npm上的lib版本 } }
要是版本号想统一管理,可以把它放到根目录的.env.production文件里:
# .env.production LIB_VERSION=1.2.3
然后构建脚本改成:
"build": "source .env.production && yarn && react-scripts build"
第三步:验证效果
- 开发时跑
yarn start:Yarn自动软链本地lib,改完代码直接生效,不用任何额外操作 - 构建时跑
yarn build:Yarn会拉取你指定的npm正式版lib,保证生产包用的是经过测试的稳定版本
三、补充:lib的版本管理和发布流程
因为lib要给4个外部项目用,建议用语义化版本(SemVer)+ Git标签来管理:
- 每次更新lib,按SemVer规则升版本:bug修复升patch(1.2.3→1.2.4),新增功能升minor(1.2.3→1.3.0),破坏性变更升major(1.2.3→2.0.0)
- 发布到npm前,给lib仓库打对应的Git标签(比如
v1.2.3),推到远程仓库,方便后续追溯和回滚 - 外部项目依赖lib时,也建议用固定版本或指定Git标签,避免自动更新踩坑
四、为什么不推荐把lib移出workspaces?
如果把lib移出workspaces,开发时你得手动跑yarn link关联本地版本,每次改完lib还要重新link或者发测试版,效率极低。而上面的方案既保留了workspaces的开发便利性,又能满足构建时的稳定性需求,完全没必要折腾移出操作。
内容的提问来源于stack exchange,提问作者llioor

