多语言版本管理:nvm、pyenv、mise 的思路

Node 要 18 又要用 22,Python 项目一个要 3.9 一个要 3.12 —— 系统自带的版本永远不够用。版本管理器就是解决这个的。

一、原理:改 PATH

所有版本管理器的核心都一样:

bash
# 安装了多个版本
~/.nvm/versions/node/v18.20.4/bin/node
~/.nvm/versions/node/v22.2.0/bin/node

# 切换版本 = 把某个目录放到 PATH 最前面
export PATH="$HOME/.nvm/versions/node/v22.2.0/bin:$PATH"

# 验证
which node && node -v

理解这一点,后面所有工具都不神秘了。

二、Node:nvm

bash
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash

# 重新登录后
nvm install 22          # 装最新版 22
nvm install 18          # 再装一个
nvm use 22              # 当前 shell 切换
nvm alias default 22    # 设默认
nvm ls                  # 看装了哪些
nvm ls-remote --lts     # 看可装的 LTS 版本
nvm uninstall 18

项目级锁定:

bash
echo "22" > .nvmrc
nvm use                 # 自动读 .nvmrc

# 或写进 package.json 的 engines 字段

nvm 是 shell 函数(不是可执行文件),所以需要 source ~/.nvm/nvm.sh,安装脚本会写进 .bashrc

三、Python:pyenv

bash
curl https://pyenv.run | bash

pyenv install 3.12.3
pyenv install 3.9.19
pyenv versions          # 列出
pyenv global 3.12.3     # 全局默认
pyenv local 3.9.19      # 当前目录(生成 .python-version)
pyenv shell 3.12.3      # 仅当前 shell

# 验证
python -V && which python

pyenv 通过 shim 机制工作:

bash
~/.pyenv/shims/python    # 一个转发脚本
# 它根据 .python-version / 环境变量决定调用哪个真实解释器

四、统一方案:mise(原 rtx)

如果同时用 Node、Python、Go、Ruby、Terraform……装一堆管理器很烦。mise 一个搞定:

bash
curl https://mise.run | sh
echo 'eval "$(mise activate bash)"' >> ~/.bashrc

mise use node@22             # 当前项目
mise use python@3.12
mise use -g node@22          # 全局
mise ls
mise ls-remote node
mise which node

# 项目里生成 .mise.toml
mise use node@22 python@3.12

.mise.toml 内容:

yaml
[tools]
node = "22"
python = "3.12"

[env]
NODE_ENV = "production"

最大优势:进目录自动切换版本(配合 mise activate),不用手动 nvm use

五、对比

工具语言机制特点
nvmNodeshell 函数改 PATH生态最成熟
fnm / voltaNode独立二进制快,volta 自动切换
pyenvPythonshim稳定
mise多语言shim + PATH统一,推荐新项目用
asdf多语言插件老牌,插件多

选择建议:

  • 只写 Node → nvm(资料最多);
  • 只写 Python → pyenv 或系统包 + venv;
  • 多语言 / 新开始mise
  • 服务器 → 尽量不用版本管理器(见下)。

六、服务器上的注意事项

这是个真实的坑:cron / systemd 里跑的任务,看不到 nvm 装的 node。

因为 nvm 是 shell 函数,只在交互式 shell 里加载。

bash
# 失败:cron 里执行 node 命令
0 3 * * * node /opt/app/run.js     # command not found

解法几种:

1. 用绝对路径(最稳)

bash
0 3 * * * /root/.nvm/versions/node/v22.2.0/bin/node /opt/app/run.js

2. 建一个软链到系统路径

bash
sudo ln -s /root/.nvm/versions/node/v22.2.0/bin/node /usr/local/bin/node
sudo ln -s /root/.nvm/versions/node/v22.2.0/bin/npm /usr/local/bin/npm

3. 在脚本里显式 source

bash
#!/usr/bin/env bash
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
nvm use 22 > /dev/null
node /opt/app/run.js

4. 生产环境更推荐:容器化或直接用官方包

bash
# 用 NodeSource 的 apt 源装系统级 node
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt install -y nodejs

我的做法:开发机用版本管理器,服务器用系统包或容器。服务器上少一层间接,就少一类诡异问题。

七、Python 的额外一层:虚拟环境

版本管理器管「哪个 Python」,虚拟环境管「这个项目装哪些包」。两者不冲突:

bash
pyenv local 3.12.3            # 定版本
python -m venv .venv          # 建虚拟环境
source .venv/bin/activate     # 激活
pip install -r requirements.txt

# 退出
deactivate

Node 的 node_modules 是项目本地的,天然隔离;Python 需要显式建虚拟环境。

八、常见问题

现象原因
command not found: nvm没 source,检查 .bashrc
换了版本但 node -v 没变PATH 里有其他 node 在前面
cron 里找不到命令shell 函数没加载(见上)
pyenv install 编译失败缺编译依赖,装 build-essential + libssl-dev 等
bash
# 排查 PATH 里到底有几个 node
which -a node
type -a node

版本管理器解决的是「同一台机器上并存多个版本」。想清楚这一点,工具选哪个都不重要。