很多做前端和Node.js开发的朋友,在Windows上折腾Node版本时,应该都有过这种体验:项目A要Node 14,项目B要Node 18,全局装了吧,切版本就得手动下载安装包,环境变量改来改去,改完还得重开终端,一不小心还会把系统搞乱。后来出现了nvm-windows,确实方便了不少,但用起来总感觉有点笨重,切换速度也不够快,有时候装着装着还报错。
我自己也是被这个问题折磨了挺久,后来换到了Fnm(Fast Node Manager),算是彻底解决了我 Windows 上的 Node 版本管理焦虑。Fnm 是 Rust 写的,天生就快,而且原生支持 Windows,配置文件清晰,切换版本基本是毫秒级体验。这篇文章我就把这套在 Windows 上安装、配置、使用 Fnm 的实操记录完整梳理一遍,包括我踩过的坑、排查过的报错,以及配合 Shell 和 VS Code 的一些优化技巧。如果你也在为 Node 多版本切换头疼,或者想从 nvm-windows 迁到 Fnm,这篇应该能帮你省不少事。
1. 为什么我放弃 nvm-windows 换到 Fnm
先说清楚 Fnm 到底是什么。Fnm 是一个跨平台的 Node.js 版本管理工具,全称是 Fast Node Manager,核心卖点就是一个字“快”。它由 Rust 编写,没有运行时依赖,安装完是一个独立的 .exe 文件,不依赖系统全局的脚本解释器,所以启动和切换版本的速度比那些基于 Shell 脚本的工具快很多。
在 Windows 上,最流行的 Node 版本管理工具其实是 nvm-windows,它是按 nvm(Linux/Mac 上广泛使用的版本管理工具)的思路移植过来的。很多教程也推荐它,但我用下来有几个特别别扭的地方:
- 切换版本后,新开的终端有时不会立刻生效,需要重启终端甚至重启电脑。
- 安装新版本时下载速度不稳定,偶尔会卡住,报错信息也不直观。
- 它的 Node 全局模块(npm 全局包)在切换版本后经常需要重新安装,因为每个版本对应独立的全局目录,切来切去很容易忘装。
- 卸载时会在环境变量里留下残留,清理起来比较麻烦。
Fnm 的出现正好解决了这些痛点。它支持.nvmrc文件(项目指定 Node 版本),支持根据目录自动切换版本,支持 node 版本别名,而且配置全部集中在一个 JSON 文件里,想备份就备份,想迁移就迁移。最关键的是,它不需要管理员权限就能装到用户目录,对没有管理员权限的公司电脑来说非常友好。
我在实测中的感受是:用 Fnm 切换 Node 版本,速度体感上是瞬间完成的,终端里敲完命令立刻就能node -v验到新版本,完全不需要关终端。这种体验和之前 nvm-windows 的“粘滞感”形成鲜明对比。
当然,Fnm 也不是没有缺点。比如它的 Shell 集成需要手动在 PowerShell 配置文件中加一段代码,如果你对 PowerShell 配置文件不熟悉,第一次配置时可能有点懵。这个我下面会详细讲,照着做就行。
2. 安装方式的选择与实操对比
Fnm 在 Windows 上提供了好几种安装方式:winget、scoop、chocolatey,以及直接下载二进制包手动安装。我实际试过 winget、scoop 和手动安装,说说我的感受。
2.1 通过 winget 安装
winget 是 Windows 10/11 自带的包管理器,Windows 10 1709 之后的版本基本都有。安装 Fnm 只需要一条命令:
winget install Schniz.fnm这条命令会自动下载 fnm 的最新 release 版本并配置环境变量。胜在省心,不用自己去官网找下载链接。不过有个小问题:winget 安装后,会让你重新打开终端才能生效,而且默认安装目录在用户目录下的AppData\Local\fnm,后续找配置的时候知道这个路径就行。
2.2 通过 scoop 安装
如果你已经装了 scoop,用 scoop 安装也很方便:
scoop install fnmscoop 的优点是它会帮你把 fnm 的可执行文件放在一个统一的目录下,后续升级直接scoop update fnm就行。它默认安装的是最新版,而且 scoop 有一个特点:部分 GUI 应用还会创建快捷方式,但 fnm 是纯命令行工具,所以只在 shims 目录生成一个链接。
2.3 手动下载二进制包
如果你不想装包管理器,或者公司电脑装软件需要审批,可以直接去 fnm 的 GitHub Releases 页面下载fnm-windows.zip。解压后你会得到一个fnm.exe,把它放到你想要的目录就可以了,比如D:\Tools\fnm。
手动安装的好处是位置完全可控,我是放在D:\Tools\fnm\fnm.exe,然后手动配置 Path 环境变量。这样做的缺点是升级时得自己再下载覆盖,没办法一条命令搞定。对于日常使用来说,其实没太大影响,反正 Fnm 本身更新频率不算高。
2.4 三种方式的优缺点速查
我想把这几种方式的特点直接放在下面这张表里供你参考:
| 安装方式 | 优点 | 缺点 | 适合场景 |
|---|---|---|---|
| winget | 自带的命令,不用额外装东西 | 安装目录藏在 AppData,要找 | 大部分人,最无脑 |
| scoop | 升级方便,和现有工具链统一 | 需要先装 scoop,有些网络环境下载慢 | 已经在用 scoop 管理工具的人 |
| 手动下载 | 路径自己定,完全可控 | 升级要手动,环境变量得自己配 | 公司电脑没管理员权限或特殊场景 |
我个人的建议是:如果你比较懒,就直接 winget。如果你想对安装位置和配置路径有绝对掌控,就手动下载。说实话,后面接 Shell 集成的时候都要手动操作一步,所以选哪种安装方式对最终配置的影响不大。
3. 环境变量配置和目录结构规划
无论你选哪种安装方式,配置环境变量这一步几乎都逃不掉。Fnm 安装后,需要两个环境变量:一个是让系统能找到fnm.exe的Path变量,另一个是指定「Fnm 安装 Node 版本和缓存的目录」的FNM_DIR变量。很多人只配了 Path,结果 Fnm 能用但下载的 Node 版本不知道被扔到哪里,后面排查起来就会绕弯路。
3.1 Path 环境变量
先说 Path。如果你用 winget 或 scoop 安装,安装工具通常会自动设置好 Path,不需要手动加。如果是手动下载解压的,需要把fnm.exe所在的目录添加到用户级别的 Path 环境变量里。
具体操作为:按 Win 键,输入“编辑账户的环境变量”,打开后选中上面的“用户变量”里的Path,点击“编辑”,然后“新建”,把 fnm.exe 的路径粘贴进去(比如D:\Tools\fnm),一路确定就好。
添加完后,新开一个终端,输入:
fnm --version如果能输出版本号,说明 Path 配置成功了。如果提示“不是内部或外部命令”,多半是环境变量没生效,或者终端没有重启,重新打开一个终端窗口再试。
3.2 FNM_DIR 环境变量
FNM_DIR 是 Fnm 用来存放所有 Node.js 版本和默认模块的目录。如果不设这个变量,Fnm 在 Windows 上会默认使用%AppData%\fnm,也就是用户的 AppData 目录下。这个目录在 C 盘,如果你的 C 盘空间紧张,或者你想把缓存和 Node 版本移到其他盘,就需要手动指定 FNM_DIR。
我就是把 FNM_DIR 指到了D:\Tools\fnm_data,理由是:C 盘是固态系统盘,攒太多 Node 版本占空间不说,万一系统重装还得重新下载。放在 D 盘的数据盘上,系统崩了也不影响这些已下载的 Node 版本。
设置方法:还是打开“编辑账户的环境变量”,在用户变量区域,点击“新建”,变量名填FNM_DIR,变量值填你期望的目录路径(例如D:\Tools\fnm_data),确定即可。注意,这里的目录不要求提前存在,Fnm 在首次运行时如果发现目录不存在会自动创建。
3.3 目录结构说明
当你设置了 FNM_DIR 并安装了一个 Node 版本后,这个目录下会生成几个子目录:
| 子目录 | 作用 |
|---|---|
node-versions | 存放所有通过 Fnm 下载的 Node.js 版本,每个版本一个子目录 |
aliases | 存放你定义的版本别名(比如default、lts-latest) |
shims | 存放接管系统命令的 shim 文件(Windows 上也有,用于目录自动切换) |
理解这几个目录的作用,对后续排查问题很有帮助。比如你切换版本不生效,先看看node-versions里到底有没有这个版本;如果设置了别名但找不到,就去aliases目录看看。总之,把 FNM_DIR 想成“Node 版本的仓库”,Path 只是“仓库入口的指示牌”,两个都要有系统才能正常运转。
4. PowerShell 集成与自动切换配置
在 Windows 上使用 Fnm,推荐搭配 PowerShell 或 Windows Terminal。这里有个关键点:如果你只想手动敲fnm use 18来切换版本,那不需要额外配置。但如果你想实现「进入某个目录,自动切换到对应 Node 版本」的体验,就必须配置 Shell 集成。这个集成的作用是,每次打开终端时,自动把 Fnm 管理的当前 Node 版本绑定到当前的 Shell 会话中。
4.1 生成系统的初始化脚本
Fnm 提供了一个命令来生成不同 Shell 的配置文件片段:
fnm env --use-on-cd其中--use-on-cd表示在切换目录时自动启用对应的 Node 版本。
把这个命令产生的输出写入 PowerShell 的配置文件$PROFILE就可以实现自动化。你不需要手动去抄文件内容,可以直接用重定向:
fnm env --use-on-cd | Out-String | Invoke-Expression但这是临时执行的写法。想永久生效,需要把这个命令加到当前用户的 PowerShell 配置文件中。在你的 PowerShell 中执行:
notepad $PROFILE如果提示找不到该文件,先执行:
New-Item -Path $PROFILE -Type File -Force然后打开的文件里,把下面这行粘贴进去:
fnm env --use-on-cd | Out-String | Invoke-Expression保存关闭后,重新打开一个 PowerShell 窗口,输入fnm current,如果能看到当前 Node 版本,就说明 Shell 集成生效了。
4.2 Windows Terminal 里新增 PowerShell 作为默认终端
Windows 11 自带 Windows Terminal,这是微软官方推荐的终端程序,支持多标签页、主题配置,和 Fnm 配合起来思路很顺。如果你在用 Windows Terminal,建议把默认终端设置为 PowerShell,然后在设置里指定启动时执行的命令行参数。
具体操作为:打开 Windows Terminal,按Ctrl+,打开设置,在“配置文件”里找到“Windows PowerShell”或“PowerShell”,点击“默认”按钮把它设为默认配置文件。设置好后,每次打开 Windows Terminal 的第一个标签页就是 PowerShell,自动加载你配置好的$PROFILE文件,Fnm 的集成也自动生效。
4.3 VS Code 集成怎么写
VS Code 是很多 Node 开发者的主力编辑器。在 VS Code 的集成终端里,如果打开终端时没有加在 Fnm,手动敲fnm use 18其实也还好,但我建议在 VS Code 设置里把 PowerShell 设置为默认终端配置文件,并确保它加载了$PROFILE。
打开 VS Code 的设置(Ctrl+,),搜索“terminal.integrated.defaultProfile.windows”,在可选列表里选择“PowerShell”。然后重新打开终端,输入fnm current测试,如果输出版本号就正常了。
可能有人会遇到 VS Code 里能fnm --version,但node命令却找不到的情况。这是因为 VS Code 的终端环境变量没有刷新。解决的办法是:完全关闭 VS Code,然后新开一个 PowerShell,确认node命令能正常用,再打开 VS Code。VS Code 启动时会继承当前环境变量,所以只要 PowerShell 里正常,VS Code 里面也跟着正常。
5. 日常安装、切换版本的实操命令
到了这一步,Fnm 基本装好、集成配好了,接下来就是高频使用环节。我把自己日常用到的 Fnm 命令整理成一个速查表,方便你以后直接对照查询。
| 命令 | 说明 |
|---|---|
fnm list | 列出所有已安装的 Node 版本 |
fnm list-remote | 列出所有远程可用的 Node 版本 |
fnm install 18 | 安装 Node.js 18 的最新版 |
fnm install 20 | 安装 Node.js 20 的最新版 |
fnm use 18 | 切换当前终端到 Node 18 |
fnm default 18 | 设置全局默认 Node 版本(新终端默认用这个) |
fnm current | 查看当前终端使用的 Node 版本 |
fnm uninstall 18 | 卸载 Node 18 |
fnm alias 18 myapp | 给 Node 18 设置一个别名(比如myapp) |
几个需要注意的命令细节:
fnm install 18只会安装 Node 18 的最新 patch 版本,比如 18.20.x。如果项目需要指定到18.16.0,直接写fnm install 18.16.0就好。fnm default 18相当于设置了默认版本,但已经打开的终端不会变,只有新开的终端才会应用默认版本。这个行为和 nvm-windows 也是类似的。- 切换版本后,全局安装的 npm 包(比如
npm install -g yarn装的 yarn)会因为 Node 版本的改变而“消失”,因为每个 Node 版本有独立的全局模块目录。这一点是 Node 版本管理工具的通用特性,不是 Fnm 的 bug。解决方案是切换到对应版本后重新安装全局包,或者用fnm exec --using=18 npm install -g yarn在指定版本下安装全局包。
顺带提一个很多人问的问题:切换 Node 版本后,npm会自动跟随吗?答案是会。Fnm 管理的是整个 Node 发行版,而 npm 随 Node 附带,所以切换 Node 版本后,npm也会自动变成那个版本对应的 npm。这在 Windows 上非常重要,因为 npm 版本差异较大时,package-lock.json的处理逻辑会有细微差异,切换版本能避免很多莫名其妙的 lock 文件冲突。
5.1 使用 .nvmrc 实现项目级版本锁定
这是 Fnm 最让我喜欢的功能之一。和 nvm(Linux/Mac)一样,Fnm 也支持.nvmrc文件。你可以在项目根目录创建这个文件,里面写上项目要求的 Node 版本号(比如18或者18.20.0),然后当你进入这个目录时,Fnm 的--use-on-cd就会自动读取这个文件并切换到对应版本。
创建 .nvmrc 的方法:
node -v > .nvmrc或者手动创建文件,写入对应版本号。我通常会在项目里固定.nvmrc文件并提交到 Git 仓库,这样团队里的其他同事 clone 下来后,第一次进入目录时自动切换版本,不会出现“我本机能跑你本机跑不了”的尴尬局面。
具体到团队协作,我建议在项目 README 里写一句“要求 Node 版本见 .nvmrc”,这样即使同事不熟悉 Fnm,也知道项目有固定版本要求。
5.2 配合 fnm exec 在单个命令中指定版本
还有一种场景:你想临时在特定 Node 版本下执行某个命令,但不想切换全局版本。比如,一个项目依赖 Node 16,但你当前默认是 Node 20。你可以用:
fnm exec --using=16 npm install这样会临时用 Node 16 执行npm install,而不会动你终端的默认版本。这个命令特别适合给老项目打补丁、或者验证某个包在不同 Node 版本下的兼容性。
6. 常见问题排查与避坑指南
这里必须分享一些我实际踩坑总结出来的经验,没有这些,你按照教程操作一遍,可能还是会遇到各种意外。这些问题如果不提前了解,可能会卡你半小时甚至一下午。
6.1 打开新终端提示fnm 不是内部或外部命令
这是 Path 环境变量没配好,或者终端没有重新打开导致的环境变量缓存问题。解决步骤:
- 先确认
fnm.exe所在目录确实在 Path 里(打开“编辑账户的环境变量”检查)。 - 确认后,新开一个终端窗口(不是新开标签页,最好是完全退出终端程序再启动)。
- 如果还不行,执行
where fnm,看系统能不能搜到。如果提示找不到,说明 Path 里没有正确包含该目录,再检查一遍路径是否写错。 - 注意区分用户变量和系统变量。如果你把 Path 加在了系统变量里,当前已经打开的终端软件(包括 VS Code)都需要完全重启才能读取到。
6.2 fnm 能列出远程版本但 install 时特别慢或失败
这在 Windows 上比较常见,源于 Fnm 默认从 Node 官方源下载,国内网络环境偶尔会不稳定。解决办法是配置镜像源。Fnm 支持通过环境变量FNM_NODE_DIST_MIRROR指定 Node 发行版镜像地址。
在设置环境变量时,新建一个用户变量:
FNM_NODE_DIST_MIRROR=https://npmmirror.com/mirrors/node/这样再执行fnm install 20时,就会从国内镜像下载,速度快很多。这个镜像源是 npm 官方在国内的同步镜像,长期可靠。
6.3 切换版本后,终端里的 node 还是旧版本
这种情况一般发生在你配置了 Shell 集成,但没有用fnm env初始化当前的 shell 会话。你敲fnm use 20,Fnm 只是修改了当前会话的环境变量,但如果 Shell 配置没有正确加载,可能不会生效。
解决方法是确认$PROFILE文件里已经有:
fnm env --use-on-cd | Out-String | Invoke-Expression然后重启终端。如果你不想改$PROFILE,也可以用临时方案:
fnm env | Out-String | Invoke-Expression手动执行一次,当前终端会立刻可用,但新终端不会保留,所以最终还是要配置$PROFILE。
6.4 系统之前装过 Node.js,Fnm 安装后 node 命令冲突
这个问题很有意思。如果你电脑上之前已经用官方安装包安装过 Node.js,安装包里会往系统 Path 里注入 Node.js 的安装目录。之后你又装了 Fnm,运行时node命令到底用哪个,取决于 Path 里哪个目录排在前面。
Fnm 的做法是,在fnm env初始化时会把 Fnm 的 shims 目录放在 Path 最前面,从而优先使用 Fnm 管理的 Node。但如果你没有配置 Shell 集成,直接敲node -v,系统找到的可能是原来的 Node.js。
我从 nvm-windows 迁移到 Fnm 时,就在这一步被坑过。系统里残留的旧 Node 安装目录一直在 Path 里,导致 Fnm 切换版本一直“看起来不生效”。后来我把旧的 Node.js 安装目录从 Path 中删除(保留文件夹作为备份),再刷新环境变量,Fnm 就完全正常了。
建议做法:在安装 Fnm 并确认它能正常管理版本后,把原来的 Node.js 安装程序卸载或者手动移除其 Path 目录。如果你有老项目依赖全局包(比如老 npm 包),可以先记录一下全局包列表(npm list -g --depth=0),然后在 Fnm 的默认版本下重新安装。
6.5 npm 全局包在切换版本后丢了
前面提到过,这是所有 Node 版本管理工具的共同特性:每个版本有独立的全局安装目录。我在切换版本时习惯用fnm exec --using=当前版本 npm install -g <包名>来在特定版本下安装包,这样切换版本后也不会丢太多东西。
我的通用做法是:列出经常用的全局包清单,在系统默认 Node 版本下统一安装一次,比如 yarn、pnpm、typescript、@vue/cli、nodemon 等。这样大部分时间用的默认版本都有这些工具,切到其他版本临时跑项目时,通常也不需要全局包。
6.6 设置 FNM_DIR 后,之前安装的版本找不到了
这是自己坑自己的一种情况:我先用默认的%AppData%\fnm安装了几个 Node 版本,后来又设置了FNM_DIR指向 D 盘。结果fnm list显示空列表,因为 Fnm 只看 FNM_DIR 里面的内容。
解决办法很简单:把原来的 Node 版本文件夹完整移动到新的 FNM_DIR 目录下,目录名保持 node-versions 不变即可。或者直接重新下载(如果网络快的话,重下也可能更省事)。
7. 我的最终推荐配置与几点心得体会
最后分享一个我目前在用的推荐配置结构,如果你完全照着做,基本可以少走很多弯路:
- 用 winget 安装 Fnm:
winget install Schniz.fnm - 设置用户环境变量
FNM_DIR为D:\Tools\fnm_data(记得用你自己的路径) - 在 PowerShell 配置文件
$PROFILE添加一行:fnm env --use-on-cd | Out-String | Invoke-Expression - 设置默认 Node 版本:
fnm install --lts&&fnm default lts-latest - 命令行工具推荐直接用 Windows Terminal 的 PowerShell 配置,关闭后重开终端
- 项目根目录统一维护
.nvmrc
这样一套配置完成后,你打开终端进入项目目录,Fnm 会自动读取.nvmrc并切到对应 Node 版本,你甚至感觉不到 Fnm 的存在——这种“隐于无形”的体验,才是一个好版本管理工具该有的样子。
在我个人的实际使用中,Fnm 最让我满意的一点是它的命令响应速度。之前用 nvm-windows 的时候,敲一条nvm use 16往往要等半秒到一秒,而 Fnm 基本是即敲即得。可能有朋友觉得半秒不算什么,但当你一天内要频繁切换版本跑不同项目的时候,这种感觉还是很明显的。
另外,Fnm 的跨平台特性也很加分。正常情况下我白天在 Windows 台式机上写代码,晚上偶尔用笔记本电脑(Linux 或 macOS)继续,两边的 Fnm 命令几乎一模一样,不用学两套工具,体验是延续的。这一点对经常在家和公司之间切换设备的开发者来说,算是隐形福利。
如果你还在用 nvm-windows 或者手动改环境变量来管理 Node 版本,我真心建议你试一下 Fnm,整个配置过程不超过十分钟,但省下来的时间绝对能值回这十分钟。最后还有一个实用小技巧:更新 Fnm 时,不用卸了重装,直接到 GitHub Releases 页面下载新版本的压缩包覆盖旧文件就行,配置文件和已安装的 Node 版本完全不受影响。