news 2026/2/27 23:12:26

从 nvm 到 fnm:一个前端老兵的版本管理工具迁移实录

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从 nvm 到 fnm:一个前端老兵的版本管理工具迁移实录

引子:那个让我崩溃的早晨

某个周一早晨,我像往常一样打开终端准备开始工作。

# 打开 iTerm2... 等待... 等待... # 终于出现命令提示符,耗时 2.3 秒

接着切换到一个需要 Node 16 的老项目:

$ nvm use 16 Now using node v16.20.2 (npm v8.19.4) # 又是 0.5 秒过去了

然后跳到另一个 Node 20 的新项目:

$ cd ../new-project $ nvm use 20 Now using node v20.19.6 (npm v10.8.2) # 再等 0.5 秒

一天下来,在 5-6 个项目间来回切换,光是等 nvm 响应就浪费了不知道多少时间。更别提有时候忘记nvm use,直接npm install导致 node_modules 版本混乱的惨剧了。

是时候做出改变了。

为什么要离开 nvm?

痛点一:Shell 启动慢得令人发指

nvm 是纯 Bash 脚本实现的。每次打开新终端,它都要:

  1. 加载nvm.sh脚本(几千行)
  2. 解析已安装的 Node 版本
  3. 设置环境变量
  4. 初始化自动补全

我用time测了一下我的.zshrc加载时间:

# 有 nvm $ time zsh -i -c exit zsh -i -c exit 0.42s user 0.23s system 95% cpu 0.678 total # 注释掉 nvm 后 $ time zsh -i -c exit zsh -i -c exit 0.08s user 0.05s system 92% cpu 0.142 total

nvm 让我的终端启动慢了近 5 倍!

痛点二:版本切换不够智能

每次进入不同项目都要手动nvm use,太原始了。虽然有avnnvm-auto等插件可以实现自动切换,但:

  • 又增加了一层依赖
  • 又拖慢了 Shell 速度
  • 配置起来也挺麻烦

痛点三:Windows 支持是个笑话

nvm 官方根本不支持 Windows。nvm-windows是另一个独立项目,命令和行为都有差异。团队里用 Windows 的同事经常遇到各种奇怪问题。

痛点四:偶发的诡异 Bug

用了几年 nvm,遇到过各种奇怪问题:

  • 全局安装的包莫名消失
  • npm prefix路径错乱
  • 多个终端窗口版本不同步
  • .nvmrc有时候不生效

遇见 fnm:Rust
带来的性能革命

fnm(Fast Node Manager)是用 Rust 写的 Node.js
版本管理器。第一次用的时候,我的反应是:

"就这?结束了?怎么这么快?"

性能实测对比

我在自己的 M1 MacBook Pro 上做了详细测试:

终端启动时间

# nvm $ time zsh -i -c exit 0.678 total # fnm $ time zsh -i -c exit 0.089 total # 提升:7.6 倍

版本切换时间

# nvm $ time nvm use 20 Now using node v20.19.6 real 0m0.347s # fnm $ time fnm use 20 Using Node v20.19.6 real 0m0.012s # 提升:29 倍

安装新版本

# nvm install 22 # 总耗时约 45 秒(包含下载) # fnm install 22 # 总耗时约 38 秒(包含下载) # 下载速度差不多,但 fnm 的解压和配置更快

为什么 fnm 这么快?

  1. 原生二进制:Rust 编译成机器码,不需要解释器
  2. 并行处理:充分利用多核 CPU
  3. 惰性加载:只在需要时才读取版本信息
  4. 高效的文件操作:Rust 的 I/O 性能本就出色

真实场景:fnm 如何改变我的工作流

场景一:多项目并行开发

我同时维护着这些项目:

项目Node 版本原因
老后台系统14历史包袱,依赖不支持高版本
主站前端18稳定的 LTS
新管理后台20需要新特性
实验性项目22尝鲜最新 API
Electron 应用18Electron 版本限制

以前用 nvm:

$ cd legacy-admin $ nvm use # 等待... Found '/Users/me/legacy-admin/.nvmrc' with version <14> Now using node v14.21.3 $ cd ../main-site $ nvm use # 又等待... Found '/Users/me/main-site/.nvmrc' with version <18> Now using node v18.20.8 # 经常忘记 nvm use,然后... $ npm install # 装了一堆错误版本的依赖 💥

现在用 fnm:

$ cd legacy-admin Using Node v14.21.3 # 自动切换,瞬间完成 $ cd ../main-site Using Node v20.19.6 # 无感切换 # 永远不会忘记切换版本,因为是自动的

场景二:CI/CD 环境统一

我们团队的 CI 配置以前是这样的:

# .gitlab-ci.yml (使用 nvm) before_script: - curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash - export NVM_DIR="$HOME/.nvm" - . "$NVM_DIR/nvm.sh" - nvm install - nvm use

换成 fnm 后:

# .gitlab-ci.yml (使用 fnm) before_script: - curl -fsSL https://fnm.vercel.app/install | bash - eval "$(fnm env)" - fnm install - fnm use

CI 构建时间减少了约 15 秒(主要是 nvm 初始化太慢)。

场景三:团队协作与跨平台

我们团队成员使用的系统:

  • 60% macOS
  • 30% Windows
  • 10% Linux

nvm 时代的痛苦:

# macOS/Linux 同事 $ nvm use 20 # Windows 同事(nvm-windows) $ nvm use 20.19.6 # 必须写完整版本号! # 而且 .nvmrc 经常不生效

fnm 时代的统一:

# 所有平台,相同命令,相同行为 $ fnm use 20

Windows 同事终于不用单独维护一套文档了。

场景四:Docker 开发环境


Dockerfile 中安装 Node:

# 以前用 nvm(不推荐,但有人这么干) RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash \ && . ~/.nvm/nvm.sh \ && nvm install 20 \ && nvm alias default 20 # 镜像体积大,层数多 # 现在用 fnm RUN curl -fsSL https://fnm.vercel.app/install | bash -s -- --install-dir /usr/local/bin \ && fnm install 20 \ && fnm default 20 # 更简洁,体积更小

场景五:Monorepo
中的多版本需求

我们有一个 Monorepo,不同 package 需要不同 Node 版本:

monorepo/ ├── packages/ │ ├── legacy-sdk/ # 需要 Node 14(兼容老用户) │ │ └── .node-version # 14 │ ├── web-app/ # 需要 Node 20 │ │ └── .node-version # 20 │ └── cli-tool/ # 需要 Node 18 │ └── .node-version # 18 └── .node-version # 20(默认)

fnm 的--use-on-cd让我在不同 package 间跳转时完全无感:

$ cd packages/legacy-sdk Using Node v14.21.3 $ cd ../web-app Using Node v20.19.6 $ cd ../cli-tool Using Node v18.20.8

完整迁移指南

第一步:安装 fnm

macOS (Homebrew):

brew install fnm

Windows (Scoop):

scoop install fnm

Windows (Chocolatey):

choco install fnm

Linux/macOS (curl):

curl -fsSL https://fnm.vercel.app/install | bash

Cargo (Rust 用户):

cargo install fnm

第二步:配置 Shell

这是最关键的一步,配置正确才能享受自动切换的便利。

Zsh (~/.zshrc):

# fnm - Fast Node Manager eval "$(fnm env --use-on-cd --shell zsh)"

Bash (~/.bashrc):

# fnm - Fast Node Manager eval "$(fnm env --use-on-cd --shell bash)"

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

# fnm - Fast Node Manager fnm env --use-on-cd --shell fish | source

PowerShell ($PROFILE):

# fnm - Fast Node Manager fnm env --use-on-cd --shell powershell | Out-String | Invoke-Expression

参数说明:

参数作用
--use-on-cd进入目录时自动读取 .node-version 或 .nvmrc 并切换
--shell <shell>指定 Shell 类型,生成对应的环境变量设置命令
--version-file-strategy recursive向上递归查找版本文件(可选)
--corepack-enabled自动启用 Corepack(可选)

第三步:迁移已安装的 Node 版本

查看 nvm 安装了哪些版本:

ls ~/.nvm/versions/node/ # v10.24.1 v14.21.3 v16.20.2 v18.20.8 v20.19.6 v22.21.0

在 fnm 中安装对应版本:

# 方式一:通过 LTS 代号安装(推荐,自动获取最新补丁版本) fnm install lts/fermium # v14.x fnm install lts/gallium # v16.x fnm install lts/hydrogen # v18.x fnm install lts/iron # v20.x fnm install lts/jod # v22.x # 方式二:通过大版本号安装 fnm install 14 fnm install 16 fnm install 18 fnm install 20 fnm install 22 # 方式三:安装精确版本 fnm install 18.20.8 fnm install 20.19.6

设置默认版本:

fnm default 22

第四步:处理全局 npm 包

这是很多人忽略的一步!nvm 下安装的全局包不会自动迁移。

查看 nvm 中的全局包:

# 切换到 nvm 的某个版本 export PATH="$HOME/.nvm/versions/node/v20.19.6/bin:$PATH" npm list -g --depth=0

在 fnm 的对应版本中重新安装:

fnm use 20 npm install -g typescript ts-node nodemon pm2 # 你需要的包

Pro Tip:可以写个脚本批量处理:

#!/bin/bash # migrate-global-packages.sh # 你常用的全局包 PACKAGES="typescript ts-node nodemon pm2 pnpm yarn" for version in 18 20 22; do echo "Installing global packages for Node $version..." fnm use $version npm install -g $PACKAGES done

第五步:注释掉 nvm 配置

编辑你的 Shell 配置文件:

# ~/.zshrc 或 ~/.bashrc # nvm - 已迁移到 fnm,注释掉避免冲突 # export NVM_DIR="$HOME/.nvm" # [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"

第六步:验证迁移结果

# 重新加载配置 source ~/.zshrc # 或 source ~/.bashrc # 验证 fnm 工作正常 fnm list fnm current node -v npm -v which node # 测试自动切换 cd /path/to/project-with-nvmrc # 应该看到 "Using Node vX.X.X" node -v

第七步:删除 nvm(可选但推荐)

确认一切正常后,可以删除 nvm 释放磁盘空间:

rm -rf ~/.nvm # 如果遇到权限问题 sudo rm -rf ~/.nvm

命令速查表

功能nvm 命令fnm 命令
安装指定版本nvm install 20fnm install 20
安装最新 LTSnvm install --ltsfnm install --lts
安装指定 LTSnvm install --lts=ironfnm install lts/iron
切换版本nvm use 20fnm use 20
设置默认版本nvm alias default 20fnm default 20
查看已安装版本nvm lsfnm list
查看远程可用版本nvm ls-remotefnm list-remote
查看当前版本nvm currentfnm current
卸载版本nvm uninstall 18fnm uninstall 18
在指定版本执行命令nvm exec 18 node -vfnm exec --using=18 node -v

LTS 版本代号参考

代号版本发布日期LTS 开始维护结束状态
Jodv22.x2024-042024-102027-04✅ Active LTS
Ironv20.x2023-042023-102026-04✅ Active LTS
Hydrogenv18.x2022-042022-102025-04⚠️ Maintenance
Galliumv16.x2021-042021-102024-09❌ End-of-Life
Fermiumv14.x2020-042020-102023-04❌ End-of-Life
建议:新项目使用 v20 或 v22,老项目尽快升级到至少 v18。

进阶技巧

1. 配置版本文件查找策略

默认情况下,fnm 只在当前目录查找.node-version.nvmrc。如果你的项目结构比较深,可以启用递归查找:

eval "$(fnm env --use-on-cd --version-file-strategy recursive)"

2. 启用 Corepack

Corepack 是 Node.js 内置的包管理器版本管理工具,可以锁定 pnpm/yarn 版本:

eval "$(fnm env --use-on-cd --corepack-enabled)"

3. 自定义安装目录

默认安装在~/.local/share/fnm,可以自定义:

export FNM_DIR="/path/to/custom/fnm" eval "$(fnm env --use-on-cd)"

4. 使用国内镜像加速

# 临时使用 fnm install 20 --node-dist-mirror=https://npmmirror.com/mirrors/node # 永久配置 export FNM_NODE_DIST_MIRROR="https://npmmirror.com/mirrors/node"

5. 在脚本中使用 fnm

#!/bin/bash # 确保 fnm 环境已加载 eval "$(fnm env)" # 使用指定版本执行 fnm use 20 node your-script.js # 或者用 exec fnm exec --using=20 node your-script.js

常见问题 FAQ

Q: fnm 安装的 Node 在哪里?

~/.local/share/fnm/node-versions/

每个版本是一个独立目录,结构清晰。

Q: 为什么which node显示的路径很奇怪?

fnm 使用"多 Shell"机制,每个 Shell 会话有独立的 PATH:

$ which node /Users/xxx/.local/state/fnm_multishells/12345_1234567890/bin/node

这是正常的,确保了不同终端窗口可以使用不同版本。

Q: 如何在 VS Code
中使用 fnm 管理的 Node?

VS Code 会自动检测 fnm。如果遇到问题,可以在settings.json中配置:

{ "terminal.integrated.env.osx": { "PATH": "${env:PATH}" } }

或者在项目根目录创建.vscode/settings.json

{{ "eslint.runtime": "node" }

Q: fnm 支持 .nvmrc 吗?

完全支持!fnm 会按以下顺序查找版本文件:

  1. .node-version
  2. .nvmrc
  3. package.jsonengines.node字段

Q: 如何回退到 nvm?

如果你想回退(虽然我不建议),只需:

  1. 注释掉 fnm 配置
  2. 取消注释 nvm 配置
  3. 重新加载 Shell

你的 nvm 数据(如果没删)还在~/.nvm

总结:值得迁移吗?

绝对值得。

迁移成本:

  • 时间:约 15-30 分钟
  • 学习曲线:几乎为零(命令高度相似)
  • 风险:极低(可随时回退)

获得收益:

  • 终端启动快 5-10 倍
  • 版本切换快 20-30 倍
  • 自动切换版本,告别手动nvm use
  • 跨平台一致性,团队协作更顺畅
  • 更少的 Bug 和怪异行为

如果你每天要打开几十次终端、在多个项目间切换,fnm 节省的时间累积起来是非常可观的。更重要的是,那种丝滑无感的体验,会让你的开发心情都变好。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/26 4:43:47

Slang光线追踪加速结构深度解析:从理论到性能优化实践

Slang光线追踪加速结构深度解析&#xff1a;从理论到性能优化实践 【免费下载链接】slang Making it easier to work with shaders 项目地址: https://gitcode.com/GitHub_Trending/sl/slang 在实时图形渲染领域&#xff0c;光线追踪技术正经历着前所未有的发展浪潮。作…

作者头像 李华
网站建设 2026/2/23 18:35:16

DeepSeek-R1-Distill-Qwen-7B集群部署终极指南:轻松搞定AI推理服务

想要快速上手高性能AI模型部署&#xff1f;DeepSeek-R1-Distill-Qwen-7B作为基于Qwen2.5-Math-7B蒸馏的推理模型&#xff0c;在数学、代码和逻辑任务中表现卓越。本文手把手教你构建稳定可靠的集群管理方案&#xff0c;让AI推理服务部署变得简单高效。 【免费下载链接】DeepSee…

作者头像 李华
网站建设 2026/2/26 19:57:40

风储调频在Matlab/Simulink中的探索:基于四机两区系统的实践

matlab/simulink 风储调频&#xff0c;风电调频&#xff0c;一次调频&#xff0c;四机两区系统&#xff0c;采用频域模型法使得风电渗透率25%&#xff0c;附加虚拟惯性控制&#xff0c;储能附加下垂控制&#xff0c;参与系统一次调频&#xff0c;系统频率特性优。 有SOC特性 特…

作者头像 李华
网站建设 2026/2/26 7:04:57

基于Java Swing的猜数字小游戏(2)

1、演示视频 基于Java Swing的猜数字小游戏2、项目截图 三、设计说明 3.1 整体架构设计 项目采用单一类封装所有功能&#xff08;GuessNumberGame&#xff09;&#xff0c;继承自Swing的JFrame类&#xff0c;遵循“界面与逻辑结合”的设计模式&#xff08;适合小型桌面应用&a…

作者头像 李华
网站建设 2026/2/25 21:21:16

提升 Web 端 JavaScript 的可信度:WAICT 体系详解

当前互联网时代&#xff0c;网页是最强大的应用平台。只要在浏览器中拥有合适的 API&#xff0c;你理论上可以安全运行任何你想运行的东西。不过——除了“加密学”这块。事实上&#xff0c;自 2011 年以来&#xff0c;“网页中的 JavaScript 加密”一说就被认为是“不靠谱”的…

作者头像 李华