锁定项目的 Node 版本
Node.js 大版本之间的 API、默认 OpenSSL、模块解析并不完全兼容。仓库没有标明该用哪一版时,有人用 18、有人用 24,故障会表现为「我这机能跑」。把版本写进仓库,再让安装在不对的版本上直接失败,比事后对 node -v 省事。
做法分三层,可以叠用:
- 告诉版本管理器装哪一版:
.nvmrc或.node-version。 - 告诉包管理器允许哪一档:
package.json的engines,必要时engine-strict=true。 - CI 读同一份声明,避免流水线和笔记本各用各的。
.nvmrc
nvm(node version manager)在 Unix、WSL、Git Bash 里切换 Node 版本。它和 n 一类工具解决的是「本机并排安装多个 Node」。
安装以 nvm 仓库 README 为准,现行脚本示例:
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.7/install.sh | bash
装完重开终端(或 source 一次配置文件)。
在项目根目录放 .nvmrc,一行一个 nvm 能识别的版本字符串,末尾换行。不要用 JSON,也不要写成 14.* 这种 npm semver 通配(nvm 不按那套解析)。
22.14.0
也可以写主版本、LTS 别名或 node(最新 Current),详见 nvm --help 里对 <version> 的说明:
22
lts/*
node
不带版本参数时,下列命令会读最近的 .nvmrc(会往上找父目录):
nvm usenvm installnvm execnvm runnvm which
本地没有该版本时,nvm install 会按 .nvmrc 下载。nvm use 只切换已安装的版本。nvm exec、nvm run 在既没有参数也找不到 .nvmrc 时,会退回到当前已激活的 Node,脚本里不要依赖这个回退。
nvm 不会在 cd 进目录时自动切换,除非你自己加了 shell 钩子(README 里有 cd 后自动 nvm use 的例子)。zsh 也可用 zsh-nvm 的 NVM_AUTO_USE。
nvm-windows 不读 .nvmrc。 它和 nvm-sh 不是同一个项目。Windows 原生更省事的是 fnm、Volta;或在 WSL 里用 nvm-sh。
engines
package.json 的 engines 声明本包适用的 Node(以及可选的 npm)范围,语法是 semver,不是 .nvmrc 那种单行别名:
{
"engines": {
"node": ">=22.0.0 <25"
}
}
范围按项目真实支持来写:只测过 22 就不要写成 *。
默认情况下,npm install 只警告、不失败,因为 npm 的 engine-strict 默认是 false。Yarn 1 会检查 engines,不匹配就退出,这才是多数人想要的。
要让 npm 也不匹配就失败,在项目根目录加 .npmrc 并提交进仓库:
engine-strict=true
版本不对时大致是:
npm ERR! engine Unsupported engine
npm ERR! engine Not compatible with your version of node/npm: ant-design-pro@1.0.0
npm ERR! notsup Required: {"node":">=14.0.0 < 15.0.0"}
npm ERR! notsup Actual: {"npm":"9.5.1","node":"v18.16.0"}
pnpm 同样认 .npmrc 里的 engine-strict。这只挡住 安装,已经装好依赖、只是换了 Node 再直接 node app.js,不会再走一遍检查。
较新的 npm 还有 devEngines,用来约束「正在开发这份源码」的人用哪套 runtime,和面向安装你这个包的用户的 engines 不是同一字段。
用脚本再挡一层
engines 管的是 install。若还要在 npm run、或别人没开 engine-strict 时拦住,可以在仓库里放一个检查脚本。适合 应用仓库;发到 registry 的库不要用 postinstall 把消费者的安装直接 exit 1,声明 engines 即可。
开发依赖装 semver:
npm i -D semver
checkver.js:
const semver = require('semver');
const { engines } = require('./package.json');
const range = engines.node;
if (!semver.satisfies(process.version, range)) {
console.error('Required node version ' + range + ', got: ' + process.version);
process.exit(1);
}
package.json:
{
"scripts": {
"postinstall": "node ./checkver.js"
}
}
postinstall 在本地 npm install 之后跑。也可以改成 prestart、pretest,只在启动和测试时检查。
CI 对齐
GitHub Actions 的 actions/setup-node 可以用同一份文件,避免 CI 写死一个和 .nvmrc 不同的号:
- uses: actions/setup-node@v6
with:
node-version-file: '.nvmrc'
node-version-file 也认 .node-version、.tool-versions、package.json(会依次看 volta.node、devEngines.runtime、engines.node)。本地声明和流水线读同一处,版本才算锁住。
常用 Node 版本管理工具
- nvm(nvm-sh):命令行安装、切换多个 Node。适用于 macOS、Linux、WSL、Git Bash。读
.nvmrc。Windows 原生请改用下面几项,或 nvm-windows(命令相近,不读.nvmrc)。nodist、nvs 也是 Windows 上的替代。 - n:交互式切换,几乎没有配置文件。适用于 macOS、Linux,不适用于 Windows。较新版本可用
n auto读.nvmrc、.node-version。 - fnm:Rust 写的,启动快。macOS、Windows、Linux 都能用。读
.nvmrc和.node-version,可配置cd进目录后自动切换。 - Volta:也跨平台。不靠
.nvmrc,用volta pin node@22.14.0把版本写进package.json的volta字段;之后在该仓库里跑node、npm会走钉住的版本。团队若要「进目录即正确版本」,Volta 比「记得敲nvm use」更硬。
asdf、mise 一类通用版本管理器用 .tool-versions(如 nodejs 22.14.0),不读 .nvmrc,除非另做插件映射。