一、为什么会遇到Node版本和Nx版本的坑
做Monorepo项目的时候,你肯定会碰到这种情况:一个项目的依赖要求用Node16跑,另一个子项目要Node18,连公共工具库都得用Node14兼容旧代码。这时候如果全局只装了一个Node版本,跑Nx命令的时候很容易炸——比如全局是Node18,跑Node16的项目,Nx会直接报错“Node版本不兼容,需要16.x但当前是18.x”。我之前做电商平台的Monorepo就踩过这个坑:每次跑不同项目的命令都要手动切nvm,上次跑工具库的单元测试忘了切回Node14,全局Node18直接把用了旧API的代码跑崩了,卡了整整半小时才排查清楚。
二、先搞懂两个核心:Nx的版本选择和nvm是什么
2.1 Nx自带的Node版本检测机制
Nx默认会校验项目的Node版本要求,一般是两个判断来源:一是项目根目录的.nvmrc文件,二是package.json里的engines字段。比如package.json里写"engines": {"node": ">=18"},如果全局Node是16,跑任何Nx命令都会提示版本错误。这个机制是对的,但问题是手动切换版本太麻烦,而且容易忘。
2.2 nvm的基本用法(适合新手)
nvm是专门帮你管Node版本的工具,不管你用mac、Linux还是Windows,都能装。比如mac/Linux用命令curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash就能装,装完后:
- 装指定版本Node:
nvm install 18.17.0 - 切换版本:
nvm use 18.17.0 - 项目里放
.nvmrc文件,cd进去的时候,终端会自动提示你切换到对应版本。但光靠自动提示还是不够,万一你开着两个终端,切错了根本没发现,所以得把nvm和Nx命令绑死。
三、集成方案:让Nx自动识别项目Node版本
3.1 方案一:给Nx配置绑定项目Node版本(最常用)
这个方案是每个子项目都放对应版本的.nvmrc,然后把Nx的命令改一下,前面加nvm exec——意思是“用当前项目.nvmrc里指定的版本,跑后面的Nx命令”。这样不管全局Node是什么,命令都不会错。
示例:修改package.json里的Nx脚本
技术栈:Shell/Bash
# 步骤1:先给项目创建.nvmrc文件,比如后台管理项目用Node18.17.0
echo "18.17.0" > .nvmrc
# 步骤2:修改package.json里的Nx脚本,把原来的"nx serve"改成带nvm exec的命令
{
"scripts": {
# 原来的旧脚本,容易踩版本坑
"old-serve": "nx serve",
# 新脚本,自动用项目指定的Node版本跑Nx
"serve": "nvm exec $(cat .nvmrc) nx serve",
# 其他Nx命令也改一下
"build": "nvm exec $(cat .nvmrc) nx build",
"test": "nvm exec $(cat .nvmrc) nx test",
"lint": "nvm exec $(cat .nvmrc) nx lint"
}
}
这个命令的逻辑很简单:cat .nvmrc把版本号读出来,比如18.17.0,然后nvm exec 18.17.0就是用这个版本执行后面的Nx命令,完全不用手动切版本。
注意:要确保nvm在脚本里能找到
有些终端的nvm是懒加载的,非交互式脚本(比如CI脚本)里可能找不到nvm,这时候要在~/.bashrc或~/.zshrc里加一行:source ~/.nvm/nvm.sh,把nvm的初始化脚本加到环境变量里,脚本就能正常调用了。
3.2 方案二:用Nx工作区统一管理多版本(复杂场景可选)
如果你的Monorepo里有几十个子项目,每个要不同版本,挨个改脚本太麻烦,可以用direnv工具配合:每个项目目录下放.envrc,写use nvm 14.21.3,direnv会自动帮你切换nvm版本,再配合Nx的全局命令,就不用每个脚本改nvm了。不过这个方案需要额外装direnv,适合有一定工程化基础的团队,普通开发者用方案一就够了。
四、实际场景举例和技术优缺点
刚才说的电商Monorepo,三个子项目:用户中心(Node16)、后台管理(Node18)、工具库(Node14),用了方案一之后,再也没出现过版本错的问题。比如我现在只要cd到apps/admin目录,npm run serve自动用Node18跑,cd到packages/utils就自动用Node14,完全不用想版本的事。
技术优缺点
- 优点:简单,不用装额外工具,改几个脚本就搞定,新手也能上手,而且每个项目的脚本都独立,不会互相影响。
- 缺点:每个脚本都要加
nvm exec,但可以用编辑器的批量替换功能,或者用脚手架(比如create-nx-workspace)自动生成带这个命令的脚本,工作量很小。
五、避坑指南和注意事项
- .nvmrc要写具体版本号,别写模糊范围:比如写
18.17.0,不要写>=18,因为nvm exec需要 exact 版本号,模糊范围会报错找不到版本。 - 别混全局Node和项目Node:全局Node尽量只放一个常用版本,比如Node20,日常写新代码用,旧项目用nvm的对应版本,不要全局装Node16、Node18,避免全局命令冲突。
- CI环境也要加nvm配置:如果项目要CI自动跑测试,也要在CI脚本里加nvm的初始化,比如GitHub Actions里,先装nvm,再跑npm命令,不然CI会找不到版本报错。
- Nx缓存可能因为Node版本失效:如果切换了Node版本,Nx的
.nx缓存可能不兼容,这时候执行nx reset清空缓存就行,第一次跑慢一点,后面就正常了。
六、总结
Monorepo里多版本Node和Nx的冲突,本质是“全局Node不匹配项目需求”,用nvm exec绑定项目的.nvmrc,是最直接、最低成本的解决方案,不用懂复杂的工程化配置,只要改几个脚本就能解决问题,适合所有用Nx的Monorepo开发者,能省掉很多版本踩坑的时间。
Comments