最近好几个同行都在问同一个问题:那些还跑在 Node.js 16 上的老项目,到底怎么干净利落地装好这个版本?官方明明已经宣布停止维护,可手里一堆存量代码就是用 16 写的,跑在 CI 上、跑在客户的服务器上,没人敢轻易动。我前前后后在各种机器上装过不下二十次 Node.js v16,Windows、macOS、Linux 都踩过坑,今天干脆把整个安装过程、版本管理、常见坑位一次性盘清楚,省得大家再走弯路。
先说清楚一件事:Node.js 16 在 2023 年 9 月已经结束了生命周期,官方不再打安全补丁。如果你是在评估新项目,我建议直接上最新的 LTS 版本。但如果你的场景是“老项目必须锁死 16”,或者某个供应商 SDK 只兼容 16,那么本文讲的所有安装方法、版本切换方案、踩坑经验,你都能直接用上。
1. 为什么还要装 Node.js 16:不是怀旧,是现实约束
1.1 Node.js 16 的生命周期回顾
Node.js 16 发布于 2021 年 4 月,同年 10 月进入 LTS(长期支持)阶段,一直维护到 2023 年 9 月。这个版本在国内外面向企业级应用的生态里存量非常大,很多基于它开发的电商中台、后端服务、工具链项目,至今还在生产环境里稳定运行。最后一个补丁版本是 16.20.2,如果你必须用 16,这个版本号就是终点,不用再等更新了。
我见过不少团队因为“官方停止维护”就想立刻升级,结果一升级依赖树就崩,尤其是那些依赖原生模块的老项目,node-gyp 一编译就是一堆兼容性报错。所以现实中“继续用 16”不是一个技术上的最优解,而是一个成本和风险控制上的务实选择。
1.2 选型判断:哪些场景必须锁定 Node 16
根据我的经验,下面几类场景基本上躲不开 Node 16:
- 老项目锁定了 npm 依赖版本,依赖树里某些包在 18 以上会有 deprecation 警告,甚至直接跑不起来。
- 项目里用了 C++ 原生模块,编译好的二进制文件只在 Node 16 的 ABI 版本下可用。Node 的 ABI 版本在 16 和 18 之间不兼容,重新编译又面临工具链升级,成本很高。
- CI/CD 流水线里的镜像和缓存全部基于 Node 16 构建,临时改基础镜像会影响发布流程稳定性。
- 外接的第三方 SDK 或内部私有包明确写明 engines 字段只能支持 Node 16。
这个判断很重要,因为安装本身不难,难的是搞清楚“你究竟需不需要装 16”。我给自己的一个原则是:能升级的项目尽快升级,不能升级的项目锁定版本并做好安全隔离,比如只在内网环境运行,不暴露公网端口。
1.3 先看清当前环境再动手
安装之前先把系统现状摸清楚,这条建议价值千金。我在帮别人排查问题时,发现很多人装了半天装不上,其实是因为机器里已经存在另一个 Node 版本,导致 PATH 优先级混乱。
建议先执行以下检查:
node -v which node npm -v如果机器里已经有 Node 在跑,先记下当前版本、安装路径和 npm 全局包列表。后面无论是覆盖安装还是用版本管理工具并行管理,都要基于这个现状来操作,不能一上来就蛮干。
2. Windows 环境下安装 Node.js 16:两种方式按需选
2.1 官方安装包方式的完整步骤
Windows 装 Node.js 最简单的方式是直接下载安装包。官方历史版本列表里可以找到 16.20.2 的安装包,文件名类似node-v16.20.2-x64.msi,下载后双击运行。
安装过程中有几步需要特别注意:
第一,安装路径不要带空格,也不要用中文目录,我见过太多因为路径带空格导致 node-gyp 编译失败的案例。建议直接安装到C:\nodejs\这种简单路径。
第二,安装向导里有个“Add to PATH”选项,必须勾上。很多人安装完成后在 cmd 里敲node -v提示找不到命令,就是因为这一步没勾。
第三,如果系统里已经装了其他版本,MSI 安装包会直接覆盖或报冲突。稳妥的做法是先卸载旧版本,再装 16,或者干脆用后面的 nvm-windows 方式。
安装完成后,重新开一个 cmd 窗口,执行:
node -v npm -v输出v16.20.2和对应的 npm 版本(Node 16 自带 npm 8,如果你看到 npm 6,说明 PATH 里优先命中了其他版本,需要检查环境变量)。
2.2 安装过程中那些容易忽略的选项
MSI 安装包除了 PATH 之外,还有几个组件选项值得关注。
- npm 包管理器:默认会装上,不要取消。
- 在线文档和附加工具(比如在 Windows 上调试用的一些工具链):如果磁盘压力大可以不装,对日常使用没影响。
- “Download additional tools” 选项:它会给 Visual Studio Build Tools 和 Python,主要是为了编译原生模块用的。如果老项目里没有 node-gyp 依赖,可以跳过;如果有,建议后面单独装一套 Build Tools,而不是让安装器自动下载,因为自动下载在国内网络环境下经常卡住。
安装包方式的优点是省心,缺点是版本切换不灵活。如果机器上只跑一个 Node 16,这种方式完全没有问题。
2.3 用 nvm-windows 管理版本:更推荐的做法
如果你预感到将来还要切回 Node 18 或 20,那就在 Windows 上用nvm-windows吧。这是一个独立的工具,和 macOS/Linux 上的nvm不是同一个项目,但使用习惯上很接近。
安装 nvm-windows 的步骤:
- 先去官方仓库下载
nvm-setup.zip或者直接下载最新 release 里的nvm-setup.exe运行。 - 安装路径同样不能有空格,建议
C:\nvm。 - 安装完成之后,nvm 会自动把自己的路径加到 PATH 里,并且会创建一个软链接目录
C:\Program Files\nodejs,这个目录就是一个指向当前激活版本的入口。
接下来用 nvm 安装 Node 16:
nvm install 16.20.2 nvm use 16.20.2 node -v这里有个细节:nvm-windows 会把已安装的版本存放在 nvm 安装目录下的v16.20.2文件夹里,然后通过修改符号链接的方式把node.exe映射到C:\Program Files\nodejs。所以如果你发现某些 IDE 找不到 Node,检查 IDE 的 Node 路径是否指向C:\Program Files\nodejs,改成这个路径就能解决。
nvm-windows 还支持设置默认版本:
nvm alias default 16.20.2这样每次新开终端都会自动使用 16,省得反复切换。
3. macOS 下安装 Node.js 16:pkg 和 nvm 的取舍
3.1 pkg 安装包方式
macOS 上最简单粗暴的方式同样是官方 pkg 安装包。下载node-v16.20.2.pkg,双击安装,一路下一步,装完打开终端敲node -v就能看到版本。这种方式会把 Node 安装到/usr/local/bin/目录下,并且自动配置 PATH。
pkg 方式的优点是完全不用动脑,缺点是卸载比较麻烦,而且版本切换没有任何余地。如果只是在一台临时机器上跑个脚本,用它够了。但是作为主力开发机,我不推荐这种方式,因为 macOS 上最容易出问题的就是全局目录权限,而 pkg 安装的 Node 在访问全局 npm 包目录时经常出现 permission denied。
3.2 用 nvm 安装并切换版本:主力开发机推荐方案
macOS 上我更推荐的方式是先用 Git 拉取 nvm 安装脚本,或者直接通过命令安装:
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash安装脚本会在用户目录下创建.nvm目录,并往 shell 配置文件(.bashrc、.zshrc)里写入环境变量加载逻辑。安装完成之后,需要重新打开终端或者source ~/.zshrc才能生效。
然后安装 Node 16:
nvm install 16.20.2 nvm use 16.20.2nvm 的机制是:所有版本都下载到~/.nvm/versions/node/v16.20.2/,然后通过修改 PATH 的方式把当前目录切到你指定的版本。它和 nvm-windows 的符号链接方案思路不同,但效果一样。
nvm 还允许你给某个项目目录写一个.nvmrc文件,文件内容就一行:16.20.2。然后进入目录执行nvm use,它会自动读取.nvmrc并激活对应版本。这个功能对维护多个项目非常实用,每个项目各自的 Node 版本互不干扰。
3.3 处理 macOS 安装时的常见问题
macOS 上有两个高频问题:
第一,curl安装 nvm 显示command not found: nvm。这通常是因为 nvm 安装脚本写入 zsh 配置时没有生效。解决办法是检查~/.zshrc里是否包含以下三行:
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。
第二,安装 Node 16 失败,提示Binary download failed。这大概率是网络问题,nvm 会从 Node 官方服务器下载编译好的二进制包,某些网络环境下下载不稳定。解决方法是设置 nvm 的镜像地址:
export NVM_NODEJS_ORG_MIRROR=https://npmmirror.com/mirrors/node/然后重新安装。这里要提醒一下,如果公司内部有网络管控,镜像地址以团队实际可用的为准,不要在博客里死记某一个地址。
4. Linux 环境下安装 Node.js 16:发行版思路别搞混
4.1 用包管理器安装:简单但版本可能不灵活
不同的 Linux 发行版对 Node.js 的默认版本策略差异很大。Debian 11 默认 Node 12,Ubuntu 20.04 默认 Node 10,Ubuntu 22.04 默认 Node 12,CentOS 7 的默认源里甚至没有 Node。直接apt install nodejs装出来的一般版本很旧,跟 16 差得远,根本不能满足需求。
所以我不推荐直接用系统默认源装 Node,除非你已经把发行版源里的版本搞清楚了,并且确定默认源里正好有 16。大多数情况下是没戏的。
Debian/Ubuntu 系统上,可以尝试使用第三方维护的 NodeSource 仓库。安装方式网上有很多资料,核心思路是先导入仓库的 GPG 密钥,然后把仓库地址写入/etc/apt/sources.list.d/,最后执行apt update和apt install nodejs。不过要提醒一句,第三方仓库的可用性和安全性需要自己评估,尤其是在企业内网环境里,能不能访问这个仓库本身就是个变数。
4.2 直接下载二进制包解压安装:最可控的方案
Linux 上我个人最推荐的方式是下载官方编译好的二进制 tar.xz 包,直接解压到指定目录,然后用软链接把命令暴露到 PATH 里。这种方式完全不依赖系统包管理器,也不会污染系统全局环境。
操作步骤:
wget https://nodejs.org/dist/v16.20.2/node-v16.20.2-linux-x64.tar.xz tar -xf node-v16.20.2-linux-x64.tar.xz mv node-v16.20.2-linux-x64 /opt/node16 ln -s /opt/node16/bin/node /usr/local/bin/node ln -s /opt/node16/bin/npm /usr/local/bin/npm这样装完后,执行node -v就是v16.20.2。顺带提一句:如果服务器要跑服务,建议把 PATH 写入/etc/profile.d/node16.sh,内容很简单:
export PATH=/opt/node16/bin:$PATH这样每次登录 shell 都会自动加载,不会出现重启后 node 命令找不到的情况。
不过软链接方案有个问题:npm 全局安装的包里面会有很多node_modules/.bin/下的可执行文件,这些文件的 shebang 通常写的是/usr/bin/env node,所以只要 node 在 PATH 里就能运行,没问题。但如果你用npm install -g安装的包带有原生二进制,比如某些 CLI 工具在安装时会编译或者绑定 Node ABI,那这种手动解压的方式同样会遇到依赖问题。解决方案很简单:安装前先把编译工具链装齐,比如build-essential或python。
4.3 Linux 上的 nvm 使用:和 macOS 基本一个套路
Linux 上同样可以装 nvm,安装命令和 macOS 一模一样,因为 nvm 本质上是 shell 脚本。安装完成后所有版本都在用户目录下,不需要 root 权限。这对服务器上多用户隔离的场景也比较友好,每个用户各管各的 Node 版本,互不干扰。
有个细节值得关注:在 Linux 服务器上,很多人习惯用 root 执行命令,导致 nvm 被装在 root 用户下,换个普通用户登录就找不到 nvm。我建议你在部署用户下安装,不要图省事用 root。Node 16 本身就停止维护了,跑在 root 下又叠加一层安全风险,不划算。
5. 安装完成后的验证与初始化:别急着 npm install
5.1 验证版本时要注意的细节
很多人验证安装就只敲一句node -v,这样不够。我习惯执行三条命令:
node -v npm -v which nodenode -v看版本,npm -v看包管理器版本,which node看当前命中的 node 到底在哪。第三点特别重要,因为环境变量 PATH 里如果多个路径都有 node,你敲node -v未必是刚装的 16。which node能帮你立刻定位实际执行的文件路径。
补充一句:Node 16.20.2 自带的 npm 版本是 8.19.x,如果你装完发现 npm 版本显示 6.x,说明你的 PATH 里命中了某些自带 npm 的系统包,优先级把刚装的 npm 盖住了。排查方向是检查/usr/bin、/usr/local/bin、用户目录下的~/.nvm/versions/node/.../bin三个路径的排列顺序。
5.2 npm 的全局目录和缓存问题
Node 16 自带的 npm 8 在目录策略上有一个明显的侧重点:它更倾向于把全局包装在当前用户目录下,而不是系统级目录。很多从 npm 6 时代升级过来的老项目,会遇到全局包完全消失的“假象”。解决方案是重新npm install -g你需要的工具包,或者把全局目录改回原来的位置:
npm config set prefix /your/desired/global/path这个配置写在~/.npmrc里,改之前建议先执行npm config ls看看当前都有哪些配置,避免误改。
另外,Linux 上如果之前用 apt 装过 Node,再手动装 16 后,npm 的缓存目录可能还指向旧路径,执行npm install时一直报错。遇到这种情况,直接清掉缓存:
npm cache clean --force再重新安装依赖,基本能解决。
5.3 老项目的依赖检查
Node 16 自带 npm 8,而很多老项目是从 npm 6 时代传下来的,它们的package-lock.json文件格式可能相对旧。首次npm install时,npm 8 会自动升级 lockfile,这个过程通常无感,但如果项目里存在大量的 git 依赖或者私有仓库依赖,且认证方式比较老,npm 8 可能会拒绝非 HTTPS 的 git 协议地址。
如果你在这个环节报错,解决办法是检查package-lock.json里有没有git+ssh://或git://开头的依赖地址,把它们改成https://或者重新配置好 SSH key。别用那种“绕过校验”的歪招,老老实实修地址是正道。
还有一个非常经典的问题:Node 16 与 node-sass 的版本匹配。如果你的项目里还是 node-sass,而不是 sass(dart-sass),那 16 只能搭配 node-sass 6.x 以上。用 4.x 或 5.x 的话,install 阶段就会因为二进制下载失败而报错。换成 sass 是长期正解,临时方案则是锁定 node-sass 版本到匹配的版本。
6. 踩坑实录:Node 16 安装与使用中的高频问题
6.1 Windows 上 node 命令找不到
先说最常见的现象:安装完成后,新开的 cmd 里敲node -v提示不是内部或外部命令。
排查步骤:
- 检查系统环境变量里有没有
C:\nodejs或你安装时指定的路径。 - 如果路径存在,检查这个目录里有没有
node.exe。 - 如果路径不存在,手动添加系统环境变量 PATH,并把 node 所在目录加进去。
还有一个隐蔽原因:MSI 安装包默认给当前用户和所有用户两种选项,如果你只在当前用户下安装了,而 cmd 是以另一个用户身份打开的,就会出现 PATH 有配置但 node 找不到的情况。用“管理员身份打开 cmd”重跑一次安装包,选择安装到所有用户,就能解决。
6.2 macOS 上安装后 version 还是旧版本
一台 Mac 上如果既用过 Homebrew,又试过 nvm,极容易出现 node 版本混乱。最常见的是 Homebrew 的 node 在/usr/local/opt/node/bin,nvm 的版本在~/.nvm下,两个路径在 PATH 里互相抢。
处理方案是把 Homebrew 的 node 卸载掉,或者调整 PATH 顺序,确保 nvm 的 shim 路径排在前面。推荐先brew uninstall node,因为如果你要长期用 nvm 管理版本,留着 Homebrew 的 node 只会制造麻烦。
6.3 Linux 上升级后 npm 彻底失效
这种情况多发生在把系统自带的 node 卸载后,又手动解压 Node 16 的时候。系统的npm可能在/usr/bin/npm里,而新解压的 npm 在/opt/node16/bin/npm,两个文件指向完全不同的内部逻辑。强行卸载旧 npm 可能导致系统的某些包管理器脚本失效。
我的做法是:不强行卸载系统自带 npm,而是用软链接把/usr/bin/node和/usr/bin/npm指到新版路径。这样系统脚本调用时也能兼容,'升级'也相对平滑。
6.4 原生模块编译失败
Node 16 环境下,node-gyp 编译原生模块时对工具链有明确要求:
- Windows 上需要 Visual Studio 2019 或更新版本,且安装 C++ 桌面开发工作负载。
- Linux 上需要
python3、make、g++。 - macOS 上需要 Xcode Command Line Tools。
如果编译报错信息里出现gyp ERR! find Python,直接检查 Python 是否在 PATH 里。很多坑都是因为系统只有 Python 3,而老版本的 node-gyp 在找 Python 2。Node 16 自带的 npm 8 内嵌的 node-gyp 兼容性已经很好了,但项目如果单独锁定了 node-gyp 版本,建议升级到一个较新的 9.x 或 10.x 版本。
6.5 常见问题速查表
| 现象 | 可能原因 | 解决方向 |
|---|---|---|
| node -v 不显示 16 | PATH 中其他版本优先级更高 | 用 which node 定位,调整 PATH 顺序 |
| npm install 卡在 idealTree | 缓存损坏或网络波动 | npm cache clean --force,重试 |
| node-sass binding 报错 | node-sass 版本不匹配 | 升级 node-sass 到 6.x+,或改用 sass |
| gyp ERR 编译失败 | 缺少编译器或 Python | 按平台安装编译工具链 |
| nvm 命令找不到 | nvm 未加载到 shell 配置 | 手动添加 NVM_DIR 相关三行到 rc 文件 |
| 运行老脚本报 OpenSSL 错误 | Node 16 的 OpenSSL 1.1.1 已到生命周期 | 评估升级项目或做网络隔离 |
6.6 一个补丁细节:16.20.2 是哪个版本
工作中有人会问,到底要装 16 的哪个小版本。我统一建议装16.20.2,因为这是官方发布的最后一个补丁版,包含 16 生命周期内所有修复。不要装 16.0.x 这种首发版,越早的版本问题越多。
7. 版本切换与长期维护的一些实操心得
7.1 团队统一版本比个人方便更重要
Node 16 安装本身不复杂,真正复杂的是让团队所有成员都用同一个版本。我在团队里推行.nvmrc+ nvm/nvm-windows 的组合方案,每个项目根目录放一个.nvmrc文件,内容写死 16.20.2。任何成员进入项目目录后,一行命令就切换到正确版本:
nvm useCI 里也同步处理,构建镜像时先执行nvm install 16.20.2 && nvm use,再跑npm ci。这一步能避免大量“本地跑得好好的,构建机上就崩”的经典纠纷。
7.2 关于 EOL 之后该不该继续用的判断
说完安装和维护,必须补一句大实话:Node 16 已经停止维护,这是事实。我的建议不是让大家赶紧升,也不是让大家永远留在 16,而是分三步走:
第一步,先把团队所有项目的 Node 版本都统一管理起来,至少做到可复现。 第二步,盘点项目依赖里最顽固的阻塞项,通常就是原生模块,针对性升级或替换。 第三步,找低峰期做一次 16 到 18 或 20 的切换演练,不要等安全漏洞被迫上线才开始动。
7.3 几个能提升幸福感的小技巧
- 全局安装
n这个包来做快速切换,虽然它不如 nvm 管理得彻底,但胜在命令简洁:n 16.20.2就能切换版本。 - Windows 用户如果嫌 nvm-windows 操作不够顺滑,可以试试用命令行脚本批量封装安装和切换逻辑。
- Linux 服务器上,给 node 命令加个 shell 别名,比如在
.bashrc里写alias node16='nvm use 16.20.2',日常操作会舒服很多。
最后分享一个我自己很受用的小习惯:每次装完 Node 16,我都会顺手写一个 Markdown 备忘录,记录这台机器的安装方式、Node 路径、npm 全局包的列表。半年后再来看这台机器,你绝对会感谢当初记录的那几行文字。毕竟 Node 16 这种已经进入维护尾声的版本,机器越多,坑越深,有条理地管理版本比临时抱佛脚重要得多。