从 nvm 到 fnm:更快、更省心的 Node.js 版本管理器迁移指南

从 nvm 到 fnm:更快、更省心的 Node.js 版本管理器迁移指南

如果你受够了每次开终端都要等上好几秒、nvm use 在新 shell 里"失忆"、Windows 上还得装 nvm-windows 这个另起炉灶的分支------那么 fnm(Fast Node Manager)值得你花十分钟迁移过来。

一、为什么需要 Node.js 版本管理器

前端生态迭代极快:一个老项目可能锁死在 Node 16,新项目却要用 Node 22 的原生 ESM 与测试运行器。手动装/卸 Node 版本既痛苦又容易污染全局。版本管理器的价值在于:

  • 一键切换nvm use 18 / fnm use 18 秒切版本
  • 目录自动切换 :进入项目目录自动读取 .nvmrc / .node-version,无需手动
  • 隔离全局依赖 :每个 Node 版本有独立的全局 node_modules,互不干扰

二、nvm:经典但显老

nvm(Node Version Manager) 是社区最老牌的方案,GitHub 仓库 nvm-sh/nvm 累计 ~81k stars

优点

  • 生态最成熟,几乎所有教程都默认你用 nvm
  • 纯 Bash/Zsh 脚本实现,零额外依赖

痛点

  • :每次启动 shell 都要 source nvm.sh,这段脚本会同步加载所有版本元数据,冷启动开销大(普遍几百毫秒到数秒)
  • Windows 不官方支持 :必须改用第三方分支 coreybutler/nvm-windows,命令和实现都和 macOS/Linux 版不一样
  • 自动切换靠手搓 hook :需要自己在 .bashrc 里写 cd 钩子才能根据 .nvmrc 自动切版本
  • 单线程 shell 脚本:下载、解压、读元数据都没有并行优化

三、fnm:用 Rust 重写的现代替代品

fnm(Fast Node Manager) 由 Gal Schlezinger 开源,仓库地址 Schniz/fnm,目前 26.4k stars ,用 Rust 编写,单一二进制文件。

核心优势

特性 nvm fnm
实现语言 Bash/Zsh 脚本 Rust 编译二进制
安装包体积 脚本(KB 级) 单二进制(~5MB)
Shell 启动开销 较大(source 脚本) 极小(仅注入一个 shell 函数)
版本切换速度 慢(同步解析) 快(并行下载 + 缓存)
Windows 原生支持 ❌ 需 nvm-windows 分支 ✅ 原生支持
.nvmrc 自动切换 需手写 cd 钩子 ✅ 内置 --use-on-cd
.node-version 支持
多架构(arm64 等) 支持 支持

简单说:fnm 把 nvm 做的事,用 Rust 重新做了一遍,而且做得更快、更跨平台、开箱即用。

四、安装 fnm

macOS / Linux(推荐 Homebrew 或脚本)

bash 复制代码
# 方式一:Homebrew(macOS / Linuxbrew)
brew install fnm

# 方式二:官方安装脚本(curl)
curl -fsSL https://fnm.vercel.app/install | bash

Windows

powershell 复制代码
# 方式一:Scoop
scoop install fnm

# 方式二:Winget
winget install Schniz.fnm

# 方式三:Chocolatey
choco install fnm

配置 Shell 集成(关键一步)

安装后必须把 fnm 的 shell 钩子加到你的启动脚本里,并开启 自动切换

Bash~/.bashrc):

bash 复制代码
eval "$(fnm env --use-on-cd --shell bash)"

Zsh~/.zshrc):

zsh 复制代码
eval "$(fnm env --use-on-cd --shell zsh)"

PowerShell$PROFILE):

powershell 复制代码
fnm env --use-on-cd --shell powershell | Out-String | Invoke-Expression

Fish~/.config/fish/config.fish):

fish 复制代码
fnm env --use-on-cd --shell fish | source

💡 --use-on-cd 是 fnm 的杀手锏:进入任何含 .nvmrc.node-version 的目录时,自动切换到对应 Node 版本;离开时自动切回默认版本。nvm 需要你手写钩子才能实现。

配置完成后重启终端,验证安装:

bash 复制代码
fnm --version

五、命令对照表(nvm → fnm 迁移速查)

这是迁移过程中最常查的部分,建议收藏:

操作 nvm 命令 fnm 命令
安装指定版本 nvm install 18 fnm install 18
安装最新 LTS nvm install --lts fnm install --lts
使用某版本(当前 shell) nvm use 18 fnm use 18
设默认版本 nvm alias default 18 fnm default 18
列出已装版本 nvm ls fnm list
列出远程可装版本 nvm ls-remote fnm list-remote
卸载某版本 nvm uninstall 18 fnm uninstall 18
查看当前版本 nvm current fnm current
执行指定版本的命令 nvm exec 18 node app.js fnm exec --using=18 node app.js
跑一次性命令 nvm run 18 --version fnm run --using=18 --version

可以看到,命令几乎是一一对应的,记忆成本极低。

六、完整迁移流程(nvm → fnm)

Step 1:记录当前 nvm 管理的版本

迁移前先摸清家底,避免遗漏:

bash 复制代码
nvm ls

记下你实际在用的版本(比如 16.20.2、18.20.4、20.18.0),以及默认版本。

Step 2:安装 fnm

按上一节的步骤安装并配置好 shell 集成,确认 fnm --version 正常输出。

Step 3:用 fnm 重新安装所需版本

bash 复制代码
# 安装你记录下来的版本(支持模糊匹配,自动装该大版本最新)
fnm install 16
fnm install 18
fnm install 20

# 安装最新 LTS 版本
fnm install --lts

# 设置默认版本(相当于 nvm alias default)
fnm default 20

fnm 的版本号支持模糊匹配:fnm install 18 会自动装 18.x 的最新版;--lts 则安装当前最新的 LTS 版本。

Step 4:迁移 .nvmrc 习惯

好消息:fnm 完全兼容 .nvmrc ,你项目里现有的 .nvmrc 文件无需任何修改。

只要开启了 --use-on-cdcd 进项目目录就会自动 fnm use 对应版本。你也可以手动触发:

bash 复制代码
fnm use            # 读取当前目录的 .nvmrc / .node-version
fnm install        # 读取 .nvmrc 并安装对应版本(首次进入新项目时很方便)

Step 5:确认全局 npm 包

注意:fnm 和 nvm 的版本目录结构不同,全局包不会自动继承。 迁移后需要在每个版本下重装你依赖的全局工具(如 pnpmyarntsxnodemon 等):

bash 复制代码
fnm use 20
npm install -g pnpm yarn tsx

如果你全局包很多,建议先在 nvm 下导出清单:

bash 复制代码
nvm use 20
npm ls -g --depth=0 > global-packages.txt

再用脚本批量重装。

Step 6:卸载 nvm(可选)

确认 fnm 一切正常后,可以清理 nvm:

1. 从 shell 启动脚本中移除 nvm 配置

编辑 ~/.bashrc / ~/.zshrc,删除或注释掉以下类似行:

bash 复制代码
# export NVM_DIR="$HOME/.nvm"
# [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
# [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"

2. 删除 nvm 目录

bash 复制代码
rm -rf "$NVM_DIR"
# 通常是
rm -rf ~/.nvm

Windows(nvm-windows) :通过"添加或删除程序"卸载,再删除 %NVM_HOME%%NVM_SYMLINK% 指向的目录,并清理环境变量 NVM_HOMENVM_SYMLINK

Step 7:验证迁移成功

bash 复制代码
# 确认 fnm 生效、node 来源正确
which node          # 应指向 fnm 管理的路径(如 ~/.fnm/...)
node -v
fnm current
fnm list

# 测试自动切换:进入一个有 .nvmrc 的项目
cd your-project/
fnm current         # 应自动切到 .nvmrc 指定的版本

七、迁移决策建议

#mermaid-svg-iF8NY1XpNuRs1HIP{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-iF8NY1XpNuRs1HIP .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-iF8NY1XpNuRs1HIP .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-iF8NY1XpNuRs1HIP .error-icon{fill:#552222;}#mermaid-svg-iF8NY1XpNuRs1HIP .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-iF8NY1XpNuRs1HIP .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-iF8NY1XpNuRs1HIP .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-iF8NY1XpNuRs1HIP .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-iF8NY1XpNuRs1HIP .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-iF8NY1XpNuRs1HIP .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-iF8NY1XpNuRs1HIP .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-iF8NY1XpNuRs1HIP .marker{fill:#333333;stroke:#333333;}#mermaid-svg-iF8NY1XpNuRs1HIP .marker.cross{stroke:#333333;}#mermaid-svg-iF8NY1XpNuRs1HIP svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-iF8NY1XpNuRs1HIP p{margin:0;}#mermaid-svg-iF8NY1XpNuRs1HIP .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-iF8NY1XpNuRs1HIP .cluster-label text{fill:#333;}#mermaid-svg-iF8NY1XpNuRs1HIP .cluster-label span{color:#333;}#mermaid-svg-iF8NY1XpNuRs1HIP .cluster-label span p{background-color:transparent;}#mermaid-svg-iF8NY1XpNuRs1HIP .label text,#mermaid-svg-iF8NY1XpNuRs1HIP span{fill:#333;color:#333;}#mermaid-svg-iF8NY1XpNuRs1HIP .node rect,#mermaid-svg-iF8NY1XpNuRs1HIP .node circle,#mermaid-svg-iF8NY1XpNuRs1HIP .node ellipse,#mermaid-svg-iF8NY1XpNuRs1HIP .node polygon,#mermaid-svg-iF8NY1XpNuRs1HIP .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-iF8NY1XpNuRs1HIP .rough-node .label text,#mermaid-svg-iF8NY1XpNuRs1HIP .node .label text,#mermaid-svg-iF8NY1XpNuRs1HIP .image-shape .label,#mermaid-svg-iF8NY1XpNuRs1HIP .icon-shape .label{text-anchor:middle;}#mermaid-svg-iF8NY1XpNuRs1HIP .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-iF8NY1XpNuRs1HIP .rough-node .label,#mermaid-svg-iF8NY1XpNuRs1HIP .node .label,#mermaid-svg-iF8NY1XpNuRs1HIP .image-shape .label,#mermaid-svg-iF8NY1XpNuRs1HIP .icon-shape .label{text-align:center;}#mermaid-svg-iF8NY1XpNuRs1HIP .node.clickable{cursor:pointer;}#mermaid-svg-iF8NY1XpNuRs1HIP .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-iF8NY1XpNuRs1HIP .arrowheadPath{fill:#333333;}#mermaid-svg-iF8NY1XpNuRs1HIP .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-iF8NY1XpNuRs1HIP .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-iF8NY1XpNuRs1HIP .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-iF8NY1XpNuRs1HIP .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-iF8NY1XpNuRs1HIP .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-iF8NY1XpNuRs1HIP .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-iF8NY1XpNuRs1HIP .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-iF8NY1XpNuRs1HIP .cluster text{fill:#333;}#mermaid-svg-iF8NY1XpNuRs1HIP .cluster span{color:#333;}#mermaid-svg-iF8NY1XpNuRs1HIP div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-iF8NY1XpNuRs1HIP .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-iF8NY1XpNuRs1HIP rect.text{fill:none;stroke-width:0;}#mermaid-svg-iF8NY1XpNuRs1HIP .icon-shape,#mermaid-svg-iF8NY1XpNuRs1HIP .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-iF8NY1XpNuRs1HIP .icon-shape p,#mermaid-svg-iF8NY1XpNuRs1HIP .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-iF8NY1XpNuRs1HIP .icon-shape .label rect,#mermaid-svg-iF8NY1XpNuRs1HIP .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-iF8NY1XpNuRs1HIP .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-iF8NY1XpNuRs1HIP .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-iF8NY1XpNuRs1HIP :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
Windows
macOS / Linux




你需要 Node 版本管理器吗?
主要平台?
✅ 强烈推荐 fnm

原生支持,告别 nvm-windows
痛点是 shell 启动慢

或自动切换失灵?
✅ 推荐迁移 fnm
团队/教程都强绑 nvm?
⚠ 可暂留 nvm

但 fnm 完全兼容 .nvmrc
📦 开始迁移

一句话总结

  • 如果你在 Windows 上 ,或受够了 shell 启动慢 / 自动切换难用 ,迁移到 fnm 收益巨大,且 .nvmrc 零成本兼容。
  • 如果你深度依赖 nvm 的某些边缘特性 、或团队强制统一,fnm 也能和平共处(两者可并存,只要 shell 启动脚本里只 eval 一个)。

八、常见坑与 FAQ

Q1:迁移后 node 命令找不到了?

A:99% 是 shell 集成没配好。确认启动脚本里有 eval "$(fnm env --use-on-cd)",并重启终端。用 which node 检查路径是否在 ~/.fnm 下。

Q2:fnm 和 nvm 能同时装吗?

A:能装,但同一个 shell 会话里只应激活一个 。如果 node 路径混乱,检查启动脚本里是否同时 source 了 nvm 和 eval 了 fnm,注释掉其一即可。

Q3:--use-on-cd 没生效?

A:确认配置时带了 --use-on-cd 参数(不是只写 fnm env)。另外,离开目录时 fnm 默认切回 fnm default 设的版本,记得先 fnm default 20 设一个默认值。

Q4:CI 环境怎么用 fnm?

A:GitHub Actions 官方有 Schniz/fnm action,或用 fnm install && fnm use 两行搞定,比 nvm 快很多。

Q5:fnm 安装包从哪下载?会不会很慢?

A:fnm 从 Node 官方源下载 Node 二进制。国内可通过设置镜像加速:

bash 复制代码
export NODE_DIST_URL=https://npmmirror.com/mirrors/node
fnm install 20

九、参考链接


写在最后 :版本管理器是开发体验里"基础设施级"的工具,换一个更快的,每天能省下的等待时间是复利。fnm 用 Rust 把这件事做到了近乎完美------跨平台、快、兼容 .nvmrc,迁移成本远小于你想象。如果你还在用 nvm,不妨今晚就试试。

相关推荐
魔力女仆2 小时前
【RUST AI】把 TTS 搬进浏览器:kokoroi-rs 的 WASM 实践
人工智能·rust·wasm
米尔的可达鸭4 小时前
UDP 通信深度实验:从 Windows FastPath 到 ICMP 端口不可达的完整排查
windows·websocket·网络协议·rust·udp·wireshark·tcpdump
jinshw5 小时前
自己实现GIS配图软件(二)
前端·后端·rust
安冬的码畜日常6 小时前
【工欲善其事】深入理解 Node.js 带并发上限的异步任务批量执行逻辑
javascript·设计模式·node.js·ai编程·异步编程·并发执行
逻极19 小时前
Rust 从入门到精通:内存安全与高性能系统编程实战
rust·系统编程·性能·内存安全
Yeauty21 小时前
渲染成图再 CLI 拼接,还是进程内直推?Rust 帧到视频的两条路
开发语言·rust·音视频
ToolReel1 天前
Node.js 查询 Google Analytics 4 数据:GA Lite API 接入实战
node.js
寒水馨1 天前
Linux下载、安装 Bun v1.3.14(附安装包bun-linux-x64.zip)
linux·javascript·typescript·node.js·bun·运行时·包管理器
兮动人1 天前
Linux 安装 Claude Code 实战:Node.js、npm、GLM 配置一次跑通
linux·npm·node.js·cc·claude code