做前端或者Node开发的朋友,应该都遇到过这种场景:新电脑刚把环境搭好,从公司仓库拉下一个老项目,跑npm install才到一半就报错,仔细一看,是node-sass编译失败。再翻一下README,项目要求Node 14,你本地装的是Node 20。这种版本错位带来的问题,在Node生态里太常见了。后来我换上了Fnm(Fast Node Manager),这类问题基本从源头上解决了。它是用Rust写的Node版本管理工具,支持Windows、macOS、Linux,可以快速安装、切换多个Node版本,还能根据项目目录自动切换对应版本。这篇文章我就把在Windows上安装、配置Fnm的完整过程,包括踩过的坑和排查方法,一次性写清楚。
1. 为什么需要Node版本管理工具,以及为什么选Fnm
1.1 多项目多版本带来的日常灾难
Node.js的版本迭代速度相当快,每隔几个月就有一个大版本发布,活跃维护版本也一直在变。可现实中的项目并不会跟着版本走,很常见的组合是:老项目锁在Node 12或14,新项目已经用上了Node 20甚至22。如果只装一个Node版本,你就被迫在“升级旧项目”和“降级新项目”之间做选择。
问题的严重性体现在几个地方。第一,老项目的原生依赖,比如node-sass、fibers、bcrypt这类模块,编译时和Node版本绑定得非常紧,Node一升级,编译直接失败,报错信息五花八门,常见的是OpenSSL 3.0相关错误。第二,有些部署脚本、CLI工具对Node版本有硬性要求,版本不对,启动就崩。第三,npm和yarn的版本行为也会随Node版本变化,装出来的依赖树都可能不一样。
很多人选择“卸载重装Node”,看起来简单,实际上非常浪费时间,而且卸载不干净还会留下各种环境变量残留,等你再次安装时,控制台里报出一堆诡异错误,排查起来特别头疼。所以,用一套工具来管理多个Node版本,让它按项目需求自动切换,才是正经解法。
1.2 常见的Node版本管理工具对比
我在选择工具时,把目前主流的几个方案都试了一遍,主要包括nvm-windows、n、Volta和Fnm。它们各有特点,但用下来差别还是很大的。
| 工具 | 实现语言 | Windows支持 | 自动切换 | 安装复杂度 | 备注 |
|---|---|---|---|---|---|
| nvm-windows | Go | 支持 | 需要手动配合脚本 | 中等 | 最常见,但有时需要管理员权限 |
| n | Node.js | 支持较弱 | 不支持 | 低 | 主要面向macOS/Linux |
| Volta | Rust | 支持 | 支持 | 中等 | 同时管理全局工具链,偏重 |
| Fnm | Rust | 原生支持 | 支持 | 低 | 速度极快,命令简洁 |
nvm-windows应该是很多人第一个用的工具,当时我也装了,但遇到过几个问题:一是切换Node版本时偶尔需要管理员权限,因为它会在系统盘创建符号链接;二是在某些Windows环境下,node会被缓存到旧路径,切换后node -v还是旧版本,需要手动清理。n这个工具在Linux和macOS上很流行,但Windows支持一直比较弱,我基本不考虑。Volta的功能很强,但它不只是版本管理,还会把npm、yarn等全局工具也一起锁定管理,如果只想管Node版本,略显笨重。
最后我选了Fnm,主要是看中它在Windows上支持得很原生,命令风格和nvm很接近,上手成本低,而且是Rust写的,执行速度肉眼可见地快。
1.3 Fnm的核心优势:快、干净、自动切换
Fnm在Windows上的体验,和我之前用的nvm-windows有质的区别。它不需要管理员权限,安装时只需要下载一个可执行文件,然后通过修改当前Shell的环境变量来切换Node版本,而不是修改系统全局的符号链接。这意味着每次切换版本影响的是当前打开的终端窗口,不会污染系统级PATH,也不会因为权限不足而失败。
另一个亮点是自动切换。只要在项目根目录放一个.node-version文件,写上20.11.1,每当你cd进入这个目录,Fnm的hook会自动检测到并切换Node版本。不用手动执行任何切换命令,省去很多操作。再加上它支持fnm default设置默认版本、fnm alias给版本起别名,日常完全够用。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Windows安装Fnm的几种方式与准备
2.1 安装前的环境检查
在开始安装之前,建议先检查一下你的Windows环境,避免装完才发现基础条件不满足。首先,操作系统建议Windows 10 1903版本以上,Windows 11当然更没问题。然后检查PowerShell版本,在开始菜单里打开Windows PowerShell,输入$PSVersionTable,能看到类似PSVersion 5.1或者7.x的输出。如果只是5.1,只要没有特殊限制,Fnm也能正常运行,但我个人建议装一个PowerShell 7或者直接用Windows Terminal,体验会好很多。
安装Fnm本身不需要Git,但如果打算用Scoop方式安装,Scoop需要Git来下载部分软件包,所以提前装好Git for Windows也不亏。另外,建议确认一下Windows的winget能不能用,在终端输入winget --version,能输出版本号就说明自带或者已安装。如果提示找不到winget,可以去Microsoft Store搜索“应用安装程序”安装,或者直接换用其他安装方式。
这里顺便说明一个概念:Fnm不像传统的安装包会把Node安装到C:\Program Files\nodejs这种路径,它会把各版本Node统一放在用户目录下的fnm目录里,按版本号区分,切换时通过环境变量指向对应版本。理解这一点,后面排查路径问题会轻松很多。
2.2 方式一:通过winget安装(最省事)
如果你的Windows满足条件,第一种安装方式最简单。打开PowerShell,先搜索一下包名,确认可用版本:
powershell复制winget search fnm
搜索结果里会出现Schniz.fnm这个包,确认后直接安装:
powershell复制winget install Schniz.fnm
安装过程会下载可执行文件并自动配置PATH,安装完成后,需要关闭当前终端窗口,再重新打开一个终端,让PATH生效。然后输入fnm --version,能看到类似fnm 1.37.0的输出,就说明安装成功了。
用winget的好处是:安装、卸载都可以通过命令管理,后续升级也比较简单:
powershell复制winget upgrade Schniz.fnm
不过我实际用下来,winget安装后需要重开终端才生效,如果你发现重开后fnm依然不是内部命令,建议先检查一下Windows Terminal是否有缓存,重启Windows Terminal或者注销一次Windows登录通常可以解决。
2.3 方式二:通过Scoop安装
如果你平时是Scoop用户,或者不想用管理员权限安装,用Scoop装Fnm也很方便。Scoop有一个特点:它默认把软件装到你的用户目录下,比如C:\Users\你的用户名\scoop\apps,整个过程不需要管理员权限,对个人开发环境来说非常友好。
如果还没有安装Scoop,可以通过下面命令安装:
powershell复制Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
irm get.scoop.sh | iex
然后安装Fnm:
powershell复制scoop bucket add main
scoop install fnm
安装完成后记得重开终端,再验证fnm --version。Scoop后续更新起来也很方便:
powershell复制scoop update fnm
需要注意,Scoop依赖Git,如果之前没装过,在安装Fnm之前需要先:
powershell复制scoop install git
否则Scoop本身可能无法工作。
2.4 方式三:手动下载压缩包安装
如果公司网络环境限制多,命令安装总是超时,可以试试手动安装。到Fnm的GitHub Releases页面下载Windows版本压缩包,文件名类似fnm-windows.zip。解压到你想要存放的目录,比如D:\Tools\fnm,里面会有一个fnm.exe。
接下来需要手动把这个目录加入系统PATH。打开“系统属性 -> 环境变量”,在“用户变量”里找到Path,点击“编辑”,新建一行,填入D:\Tools\fnm,确定保存。然后关掉当前终端,重新打开一个,执行fnm --version验证。
手动安装的好处是你能完全掌控安装位置,方便公司电脑上做统一部署;缺点是以后升级版本要自己下载覆盖,稍微麻烦一点。
2.5 安装后的验证与基础配置
无论用哪种方式,安装完成后都要做一次验证。重新打开终端,依次执行:
powershell复制fnm --version
能看到版本号说明可执行文件没问题。再执行:
powershell复制fnm list
或者简写为:
powershell复制fnm ls
这个命令会列出已经安装的Node版本,如果目前还没装过Node,输出会是空的,这属于正常现象。接下来不要急着安装Node,先把Shell环境配置好,否则即使装了Node,终端也无法自动识别Fnm的环境变量。
3. 初始化Shell环境:PowerShell配置细节
3.1 Fnm环境变量与hook的工作原理
Fnm切换Node版本的原理,和普通的版本管理工具不太一样。它并不是去修改系统中的某个符号链接,而是通过fnm env生成一段Shell脚本,在Shell启动时把Fnm相关目录和当前默认Node版本的路径插入到PATH环境变量里。
这里有一个关键参数--use-on-cd,它的作用是注册一个目录变化时的钩子。当你从终端进入某个目录时,Fnm会检查这个目录下是否存在.node-version文件或.nvmrc文件,如果存在,就自动切换到文件里指定的Node版本。这就是“自动切换”背后的机制。
理解了这个原理,你就能明白为什么很多人配置完PowerShell后,不一定马上生效,因为这一行脚本是写在PowerShell配置文件里的,而配置文件只有在PowerShell启动时才会执行。如果你改完配置没有重开终端,自然就不会生效。
3.2 PowerShell配置步骤(含执行策略处理)
配置PowerShell首选方式是修改$PROFILE。首先打开PowerShell,查看当前的配置文件路径:
powershell复制echo $PROFILE
如果系统提示文件不存在,先执行下面命令创建它:
powershell复制New-Item -ItemType File -Path $PROFILE -Force
然后用记事本打开:
powershell复制notepad $PROFILE
在文件末尾添加下面这行:
powershell复制fnm env --use-on-cd | Out-String | Invoke-Expression
保存关闭,然后在终端里执行:
powershell复制. $PROFILE
重新加载配置文件,验证是否生效。如果一切正常,你可以输入:
powershell复制fnm current
此时可能提示未安装Node版本,但你至少能看到Fnm已经被正确加载。
这里有个小坑:很多人会用fnm env --use-on-cd | Invoke-Expression,而不加Out-String。在PowerShell 5.1下,原生命令输出的是多行字符串数组,直接传给Invoke-Expression时,偶尔会因为分步执行导致变量定义失败。官方推荐的做法就是先Out-String合并成一段文本再执行,我照着做了之后再也没遇到过这类问题。
如果修改完配置文件后,PowerShell提示“无法加载配置文件,因为在此系统上禁止运行脚本”,这是执行策略限制导致的。可以运行下面命令:
powershell复制Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
然后重新加载配置文件。这条命令只需要给当前用户授权即可,不需要管理员权限。
3.3 为CMD和Git Bash配置(可选)
虽然日常使用PowerShell比较多,但难免有同事或服务器环境需要用CMD或Git Bash。Fnm对两种环境都有支持,不过配置方式略不同。
在CMD窗口里,执行fnm env --use-on-cd,会输出一段批处理脚本。你可以把这段脚本内容保存成一个.bat文件,以后每次要用Fnm时先执行它,或者把它加到系统环境变量的AutoRun项里,这需要修改注册表,动手能力不强的朋友不建议折腾。平时偶尔用一次的话,直接在CMD里手动执行fnm env --use-on-cd就足够了。
在Git Bash里更简单。打开Git Bash,编辑~/.bashrc文件:
bash复制echo 'eval "$(fnm env --use-on-cd)"' >> ~/.bashrc
source ~/.bashrc
之后每次打开Git Bash就能自动加载Fnm。需要注意的是,Fnm官方优先支持的是PowerShell,在Git Bash下虽然能用,但个别版本可能存在兼容性问题,如果发现自动切换不生效,优先检查Git Bash的路径映射。
3.4 验证自动切换是否生效
配置完成后,最好做一个简单的验证,确认自动切换真的没问题。我先创建两个测试目录,比如D:\test\node14和D:\test\node22。然后在node14目录下新建一个.node-version文件,内容写14.21.3;在node22目录下新建一个.node-version文件,内容写22.11.0。
接下来先在终端里安装这两个版本的Node:
powershell复制fnm install 14.21.3
fnm install 22.11.0
安装完成后,进入node14目录,再执行node -v,应该输出v14.21.3;切换到node22目录,再执行node -v,应该自动变成v22.11.0。如果切换没生效,大概率是hook配置有问题,可以回到上一节检查$PROFILE。
4. Node版本安装、切换与日常管理实战
4.1 安装指定版本、LTS版本与查看远程版本
Fnm安装Node版本非常直接。要安装最新的LTS版本,执行:
powershell复制fnm install --lts
要安装某个具体版本号,比如Node 20.11.1,执行:
powershell复制fnm install 20.11.1
如果你不确定有哪些版本可用,先查看远程版本列表:
powershell复制fnm ls-remote
这个命令会输出当前所有可安装的版本号,有一些是LTS,有一些是当前版本,数量很多,你可以通过命令行过滤:
powershell复制fnm ls-remote | Select-String "20"
这样能快速筛选出Node 20.x的所有版本。在Windows上第一次执行ls-remote时,会去Node官方源拉取版本列表,如果网络环境不太好,可能会比较慢或者超时。这时候可以设置镜像源来加速,后面第5节会专门讲。
安装完成后,用fnm ls查看本机已安装的所有版本:
powershell复制fnm ls
输出里会列出已安装的版本,并在当前正在使用的版本旁边加一个标记,非常直观。
4.2 临时切换与设置默认版本
Fnm的切换分成两种情况:临时切换当前终端窗口的版本,和设置全局默认版本。
临时切换的命令是:
powershell复制fnm use 20.11.1
这个命令只会影响当前终端窗口,不影响其他窗口。当你在一个终端里反复测试不同Node版本时,这个功能很方便。如果你在项目目录下执行,也可以配合自动切换配置一起用,效果叠加。
设置默认版本的命令是:
powershell复制fnm default 20.11.1
执行后,以后新开的终端窗口都会默认使用这个版本,除非你手动切换或通过项目级.node-version覆盖。如果你希望每次新打开终端时都使用最新的LTS版本,也可以直接执行:
powershell复制fnm default --lts
这样就不需要关心LTS版本号具体是多少,跟着官方最新LTS走。
还有一个很实用的组合参数--install-if-missing,配合fnm use使用:
powershell复制fnm use 18.20.0 --install-if-missing
如果这个版本没安装,Fnm会先自动下载安装,再切换过去,省了一步操作。
4.3 版本别名与常用命令速查
项目多了以后,我习惯给常用版本起一个别名,比如把长期维护的Node 18版本命名为v18,这样切换时就不用记完整版本号。
powershell复制fnm alias 18.20.0 v18
fnm use v18
查看当前所有别名:
powershell复制fnm aliases
不需要某个别名时,删除即可:
powershell复制fnm unalias v18
这里还有一个实用的命令fnm current,用来查看当前终端正在使用的Node版本:
powershell复制fnm current
在日常脚本里,我经常用fnm current来判断切换是否生效。总结一下常用命令:
| 命令 | 作用 |
|---|---|
fnm ls |
列出本地已安装的Node版本 |
fnm ls-remote |
列出远程可安装的Node版本 |
fnm install 20.11.1 |
安装指定版本 |
fnm install --lts |
安装最新LTS版本 |
fnm use 20.11.1 |
临时切换当前终端版本 |
fnm default 20.11.1 |
设置默认版本 |
fnm current |
显示当前使用的版本 |
fnm alias 20.11.1 v20 |
给版本起别名 |
fnm uninstall 20.11.1 |
卸载指定版本 |
fnm env --use-on-cd |
输出自动切换所需的环境配置 |
4.4 项目级.node-version实现自动切换
自动切换是Fnm最有价值的功能之一,甚至可以算是我选择它的决定性因素。配置方式很简单:在项目根目录新建一个.node-version文件,内容只写版本号,比如:
code复制20.11.1
也可以写类似20这样的前缀版本,Fnm会自动匹配本机已安装的20.x最新版本。如果你想保留nvm生态的习惯,也可以用.nvmrc文件名,Fnm同样支持,但匹配优先级略低于.node-version。
当你在项目目录里打开终端,Fnm的hook检测到配置文件,就会自动切换Node版本。这样不同项目之间,不需要记任何命令,打开哪个项目目录,Node版本就是哪个。团队成员如果有同样的配置,也能避免“在我电脑上是好的”这种问题。
我建议把这个文件提交到git仓库,和package.json放在一起。这样新同事clone项目后,只要装好Fnm,进入目录自动就用了对的Node版本,不用再翻文档看项目要求。
4.5 与npm、yarn、pnpm及全局工具的搭配
使用Fnm切换Node版本后,npm版本是跟着Node版本走的,所以不同Node版本对应的npm版本不同,这是正常现象。如果你使用yarn classic,通常也是跟随Node的全局路径走。这里有一个需要特别注意的点:全局安装的npm包,比如@vue/cli、nodemon、pm2,在不同Node版本之间是不共享的。因为每个Node版本都有自己独立的全局目录,切换版本后,你会发现之前安装的全局命令不见了。
这其实是个好设计。有些工具对Node版本敏感,比如老版本的node-sass,如果用错了Node版本,全局安装反而会污染环境。我自己的做法是:给每个经常用的Node版本装好对应版本的必要全局工具,再配合package.json里的engines字段声明项目需要的Node版本范围,减少团队配合时的版本冲突。
用pnpm的话,建议启用corepack并开启自动安装:
powershell复制corepack enable
这样pnpm版本会由项目的packageManager字段控制,和Node版本管理互不干扰。
4.6 实测场景:多项目并行开发的日常
光讲命令不够直观,我拿自己的一次实际经历来演示。我电脑上有两个项目,一个是维护了很多年的老项目,用的是Node 14.21.3,依赖里有node-sass;另一个是最近在做的项目,用的是Node 22.11.0,依赖里用到了新版Vite。
以前用nvm-windows的时候,我每天都要在终端里手动nvm use 14、nvm use 22来回切,偶尔忘记切,跑起老项目就报错。现在用Fnm,我在老项目根目录创建了一个.node-version文件,内容14.21.3,新项目根目录创建了.node-version文件,内容22.11.0。
然后我分别进入两个目录,不用执行任何切换命令,node -v自动显示对应版本。如果开两个终端窗口,一个在老项目跑开发服务,一个在新项目跑编译,两者互不干扰。这种体验最明显的好处是:你再也不用靠脑子记当前用的是哪个版本,目录一进去就自动归位。
5. 常见问题与排查技巧实录
5.1 安装版本时提示“not yet released or is not available”
这个报错在Fnm里很典型,比如你输入fnm install 24.19.0,它提示类似Node.js v24.19.0 is not yet released or is not available。原因一般是两个:要么版本号写错了,要么这个版本确实还没有正式发布,只是测试版或内部版本。
解决方法是先执行fnm ls-remote查看当前远程可用的版本列表,确认你要安装的版本确实在列表里。如果你只是想要最新的LTS,直接用fnm install --lts最省心。另外,Fnm默认从Node官方源获取版本信息,如果网络原因导致版本列表没有及时更新,也会出现明明已经存在的版本却提示不可用。这时候可以在PowerShell里执行:
powershell复制fnm ls-remote
多刷两次,或者设置镜像源后再试。
5.2 提示“fnm 不是内部或外部命令”
这个问题最常出现在刚安装完、还没重开终端的场景。原因是PATH环境变量没有刷新。先关掉当前终端,重新打开一个试试。如果还不行,检查一下安装目录是否真的在PATH里。可以用下面命令查看PATH中包含的目录:
powershell复制$env:Path -split ';' | Select-String "fnm"
如果没有输出到任何内容,说明安装方式没有自动写入PATH,或者写入失败。这时需要手动到“系统属性 -> 环境变量”里,把Fnm的可执行文件目录添加到用户变量的Path中。如果之前用winget安装的,找一下Fnm安装到了哪个目录,常见的是%LOCALAPPDATA%\Microsoft\WinGet\Packages\Schniz.fnm_...下面,把对应路径加进去即可。
5.3 修改PowerShell配置后提示执行策略受限
我在公司电脑上配置$PROFILE时,经常遇到执行策略限制,明明在个人电脑上好好的,到了公司管理严格的机器上就提示“禁止运行脚本”。这是因为默认执行策略是Restricted,连用户自己的配置文件都不允许执行。
解决办法是给当前用户设置RemoteSigned策略,这个策略允许本机创建的脚本运行,从网络下载的脚本必须有签名:
powershell复制Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
执行时需要确认一次,输入Y回车即可。设置完成后,再执行. $PROFILE重新加载,应该就不会报错了。如果公司策略要求更严格,不允许你修改执行策略,那只能退而求其次,每次打开PowerShell手动执行fnm env --use-on-cd | Out-String | Invoke-Expression,也能在会话内使用Fnm,只是每次都要敲一遍,稍显麻烦。
5.4 切换版本后node -v没变化
这个问题之前用nvm-windows时也遇到过,换成Fnm后偶尔也会出现。首先检查是不是终端窗口没有重新加载配置文件,执行一下:
powershell复制. $PROFILE
然后查看当前node命令的实际路径:
powershell复制Get-Command node | Select-Object Source
如果路径显示的是C:\Program Files\nodejs\node.exe,说明系统PATH里还残留着旧Node的路径,Fnm通过修改PATH切换版本的能力被旧路径干扰了。解决方法是到环境变量里检查Path,如果包含旧的Node安装目录,删掉它,确保PATH中优先使用的是Fnm的多Shell临时目录。如果项目里有.node-version文件但没触发切换,检查文件名是否正确,注意是.node-version,不是.node_version,中间是英文短横线。
5.5 需要卸载或清理残留时怎么办
如果以后不想用Fnm了,或者要换新电脑,正确卸载可以避免残留。先执行:
powershell复制fnm uninstall 20.11.1
把已安装的Node版本一个个卸载掉。然后从$PROFILE中删除fnm env --use-on-cd | Out-String | Invoke-Expression这一行。接着删掉Fnm的可执行文件,一种是卸载winget包:
powershell复制winget uninstall Schniz.fnm
或者手动删除解压目录。最后检查环境变量里是否有Fnm相关路径,比如FNM_DIR、FNM_NODE_DIST_MIRROR,以及Path里的Fnm目录,全部清理干净。默认的FNM_DIR一般在%LOCALAPPDATA%\fnm,如果没设置过,可以直接删掉这个目录,里面保存的是下载过的Node版本缓存。
5.6 网络不好时的下载加速技巧
最后分享一个几乎所有Windows用户都需要的技巧。Fnm默认从Node官方源下载Node压缩包,在国内网络环境下,下载速度不稳定是常态,有时候一个版本下载到一半就失败了。解决办法是设置镜像环境变量。
在PowerShell里执行下面命令,临时生效:
powershell复制$env:FNM_NODE_DIST_MIRROR = "https://npmmirror.com/mirrors/node/"
想永久生效,用setx写入用户环境变量:
powershell复制setx FNM_NODE_DIST_MIRROR "https://npmmirror.com/mirrors/node/"
设置完成后,之后执行fnm install 20.11.1时,会从镜像源下载,速度会明显提升。这个变量不影响的现有版本和切换操作,所以可以放心配置。
我个人在实际操作里,还会在$PROFILE里顺手加一行fnm default --lts,这能保证新开的终端默认使用最新LTS版本。再配合.node-version自动切换,日常开发基本不需要碰版本切换命令了。Fnm这个工具,看似只是把Node版本管理这件事做快了,用久了你会发现,它真正解决的是多项目并行时代最琐碎也最恼人的一致性问题。上面这套流程,是我在Windows上反复重装环境后沉淀下来的版本,照着走一遍,基本不会出错。
