ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Mac上Node版本管理实战:nvm从入门到精通,解决你的环境难题

Mac上Node版本管理实战:nvm从入门到精通,解决你的环境难题 早就想写一篇关于 Mac 上 Node 版本管理的实操总结了。这两年我身边不少同事和社群里的朋友都被 Node 版本折腾得够呛老项目跑不起来、新项目装不上依赖、全局包装了一堆却不知道归谁管。我自己也踩过不少坑从最早用安装包直接装 Node到后来被权限问题搞得焦头烂额再到老老实实用 nvm 统一管理整个过程走下来最深的一个体会就是在 Mac 上做 Node 开发nvm 不是选择题而是必答题。这篇内容我会从“为什么需要 nvm”讲起再到安装、配置、日常使用和问题排查全程用自己的实际经验说话。如果你正准备开始 Node 开发或者已经被版本问题折磨到想重装系统这篇文章应该能帮你省下不少时间。1. 动手之前先搞懂为什么你的 Mac 需要一个 nvm1.1 那些年被 Node 版本折磨的场景很多初学者甚至一些工作两三年的前端都有过这种经历电脑里装了一个 Node也不知道是什么版本反正项目能跑就行。直到某一天你拉下一个新项目npm install报了一堆错看报错信息大概率是engines字段要求 Node 版本不低于某个值或者你维护的老项目突然要更新依赖结果一升级运行时报SyntaxError: Unexpected token ?然后你才发现Node 版本太新语法不兼容。我印象最深的一次是2021 年的时候我需要同时维护一个基于 Vue 2 的老后台系统和一个小程序的构建工具链。老系统要求 Node 12小程序工具链要 Node 14 以上两端都得跑。那时候我电脑里装的是 Node 14每回切项目都要小心翼翼地看构建脚本后来实在受不了才下定决心换了 nvm。换完之后这种“按项目切换 Node 版本”的事情就变成了几秒钟的命令操作。还有一类更隐蔽的问题用sudo npm install -g装全局包。用 sudo 就说明你的 Node 安装在系统目录里权限不够才会要加 sudo。这种方式短时间能跑但下次升级 Node 的时候全局包大概率就废了还要重新装一遍。而家里有 nvm 的人全局包跟着对应的 Node 版本走想换版本就切换几乎不用操心这些事。1.2 nvm 到底帮你做了什么nvm 的全称是 Node Version Manager它做的核心事情就是允许你在同一台 Mac 上安装多个互不干扰的 Node 版本随时切换当前生效的版本。它的底层原理并不复杂nvm 会把你安装的所有 Node 版本放在一个独立的目录下默认是~/.nvm/versions/node/然后在你的 shell 配置里写一段脚本。每次你在终端里执行nvm use 16的时候nvm 会修改当前 shell 的PATH环境变量把对应的 Node 版本目录放到最前面。这样当你敲node或者npm的时候系统找到的就是你指定的那个版本。听起来不算黑科技但这个设计非常实用。它把“不同项目对 Node 版本的不同需求”这件事变成了一种全局和项目级都能精细管控的方案。全局层面你可以用nvm alias default设定一个默认版本项目层面每个项目里可以写一个.nvmrc文件一行命令就切到项目需要的版本。对于多项目并行开发、CI 环境一致性校验甚至新版 Node 特性尝鲜都非常好用。1.3 同类工具对比为什么是 nvm 而不是其他方案市面上 Node 版本管理工具并不只有 nvm 一个常见的还有 n、fnm、volta。我个人的建议是Mac 上优先选 nvm。工具安装方式特点适用场景nvm官方脚本 / Homebrew老牌、社区成熟、支持.nvmrc、按 shell 切换绝大多数 Mac 开发者的默认选择nnpm 全局包安装需要先有一个可用的 Node 才能装存在“先有鸡还是先有蛋”的问题简单需求、对多版本切换要求不高的人fnm直接下载二进制基于 Rust速度快与 nvm 用法高度相似追求极速、愿意折腾新工具的人volta可直接安装除了版本切换还能锁定 npm/yarn 版本团队协作更友好团队统一工具链、依赖锁定需求较强的场景如果你是刚开始接触版本管理的小白建议直接上 nvm。理由很简单教程多、问题案例多、各大社区讨论多遇到问题基本一搜就有答案。官方脚本安装方式虽然要手动配置环境变量但正是这个过程能帮你搞清楚终端的工作原理对后面排查问题反而有帮助。2. 安装前准备先把“前任”打扫干净2.1 检查当前环境判断你到底处于什么状态在安装 nvm 之前我强烈建议你先花两分钟看一下自己电脑目前的 Node 状态。你可以打开终端依次执行下面几条命令node -v npm -v which node which npm注意看which node的结果。如果输出是/usr/local/bin/node或/opt/homebrew/bin/node说明你是通过官方安装包或者 Homebrew 安装的 Node这种属于“系统级”安装可能后续会有权限问题和版本切换的干扰。如果输出node: command not found那说明你还没有装过 Node反而是最简单干净的状态可以直接跳到安装 nvm 那一步。还有一种情况你曾经手动下载过二进制包解压后配置过PATH这时候which node可能指向你自己的目录比如/Users/你的用户名/node/bin/node。如果是这种情况安装 nvm 之前记得把~/.zshrc或~/.bash_profile里手动配置的 Node 相关路径注释掉否则后面版本切换会被旧的 PATH 设置抢先。2.2 卸载旧 Node别偷懒这一步很关键如果你已经确认电脑里存在用 Homebrew 安装的 Node可以这样卸载brew uninstall --force node顺便把之前可能残留的全局包目录也清理一下# 确认是否存在 npm 全局包目录 ls -la /usr/local/lib/node_modules ls -la /opt/homebrew/lib/node_modules如果是用官方 pkg 安装包装的 Node卸载方式稍微麻烦一点。官方安装包会在系统里放一堆文件建议去官网下载对应版本的 pkg 安装包再次运行后安装器里通常会有“卸载”选项或者手动删除下面的路径如果存在sudo rm -rf /usr/local/include/node sudo rm -rf /usr/local/lib/node_modules sudo rm -rf /usr/local/bin/node sudo rm -rf /usr/local/bin/npm sudo rm -rf /usr/local/bin/npx还有一类常见残留是~/.npm目录这是 npm 的缓存和全局配置目录。我的建议是如果确定要重新走 nvm 这条路备份一下~/.npmrc里的自定义配置比如公司私有镜像源然后直接把~/.npm删掉避免旧配置干扰新环境。2.3 搞定 Homebrew你的安装前置条件nvm 的官方推荐安装方式其实不依赖 Homebrew但在 Mac 上很多人的 Homebrew 环境本身是好的所以这里顺带说一句 Homebrew 的准备。如果你还没有 Homebrew可以先装一个。这是 Mac 上最流行的包管理器后面很多工具链比如 Python、Redis、FFmpeg都可能用到它。/bin/bash -c $(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)安装完成之后建议先运行brew doctor检查一下环境是否有问题。如果你在安装 Homebrew 时遇到网络慢或者下载失败的问题常见原因是访问 GitHub 资源不稳定这属于网络环境问题可以考虑搭配国内镜像源进行安装这个在各类社区教程里都有成熟方案这里就不展开了。我的建议是nvm 的安装可以不依赖 Homebrew但 Homebrew 本身值得装。因为后面你在 Mac 上做开发还会用到很多其他命令行工具有 Homebrew 统一管理会方便很多。3. 安装 nvm 并完成全局配置3.1 两种安装方式我建议你选官方脚本nvm 的安装途径主要有两种官方 shell 脚本和 Homebrew。先说结论我更推荐用官方 shell 脚本安装。用 Homebrew 装 nvm 的问题是它只是在 Homebrew 的目录里放了一份 nvm 的脚本官方 README 也明确说了用 Homebrew 安装之后你还得手动在 shell 配置里加载它并不比官方脚本省事而且后续升级 nvm 时还要用 brew 来更新多了一层依赖。官方脚本的方式很简单一条命令curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash如果你访问 GitHub 不稳定导致下载失败可以尝试把raw.githubusercontent.com替换为镜像地址具体地址建议从 nvm 官方仓库的 README 里获取最新信息或者使用 gitee 上的同步仓库也可以。这里有一点要注意nvm 的版本号要选最新的建议去 nvm 的 GitHub releases 页面看一眼当前最新版本是哪个然后把命令里的v0.39.7替换成对应版本号。3.2 环境变量配置为什么打开终端还是提示 nvm: command not found这是 nvm 安装之后最常见的坑。官方脚本安装完如果一切顺利脚本会自动在你当前的 shell 配置文件里追加几行 nvm 加载代码。但如果你用的是 zshmacOS 默认配置文件是~/.zshrc如果你用的是 bash配置文件是~/.bash_profile或~/.bashrc。脚本有时候会识别不准导致没有写入正确的文件。解决方法是手动打开你的 shell 配置文件检查一遍# 如果用的是 zsh vi ~/.zshrc # 如果用的是 bash vi ~/.bash_profile正常情况下文件末尾应该有这样几行export NVM_DIR$HOME/.nvm [ -s $NVM_DIR/nvm.sh ] \. $NVM_DIR/nvm.sh # This loads nvm [ -s $NVM_DIR/bash_completion ] \. $NVM_DIR/bash_completion # This loads nvm bash_completion如果没有就把它们手动加进去然后执行source ~/.zshrc或重新打开一个终端窗口。这时候输入nvm --version如果能看到版本号说明环境变量已经生效了。顺便说一个我自己的习惯如果你偶尔会用 bash 执行脚本我也建议在~/.bash_profile里同样写上这三行。因为有些 CI 脚本、自动化任务默认用的是 bash如果 nvm 在 bash 里没有加载脚本运行时会报nvm: command not found排查起来比较费劲。3.3 验证安装确认 nvm 能正常管理版本环境变量配置好之后你需要验证 nvm 是否能正常安装和切换 Node 版本。先看一下 nvm 列表nvm ls如果显示no installed versions说明 nvm 本身工作正常只是还没有安装任何 Node 版本。这时候你可以装一个当前最新的 LTS长期支持版建议生产环境使用nvm install --lts安装完成之后运行node -v和npm -v如果能看到对应的版本号说明整套环境已经跑通了。这里我还想说一个细节安装 Node 时nvm 会同时安装对应版本的 npm所以你不必担心 npm 缺失的问题。而且 npm 的全局包也会被隔离在当前 Node 版本目录下不会污染其他版本这一点非常舒服。4. nvm 日常使用手册装、切、删、设默认4.1 安装与切换 Node 版本核心操作就那么几条命令nvm 日常最常用的命令其实不超过十个。我来按使用频率排一个清单覆盖大部分场景。# 查看远程所有可用版本 nvm ls-remote # 查看远程 LTS 版本列表 nvm ls-remote --lts # 安装指定版本 nvm install 16.20.2 nvm install --lts # 查看本地已安装版本 nvm ls # 切换当前 shell 的版本 nvm use 16.20.2 nvm use --lts # 设置默认版本 nvm alias default 16.20.2 # 给版本设置一个别名 nvm alias old-project 12.22.12 # 卸载指定版本 nvm uninstall 16.20.2具体到操作节奏我建议你按这个套路来第一步先nvm ls-remote --lts看一眼现在 LTS 版本到哪个版本号了一般前端项目依赖对 LTS 兼容性最好。第二步nvm install 版本号安装。第三步nvm use 版本号切换。第四步如果确认这个版本是你长期要用的用nvm alias default 版本号固定为默认。切换版本的时候有件事要记住nvm use只对“当前这个终端窗口当前 shell 会话”生效。你新开一个终端生效的又会变成 default 版本。这不是 bug是设计如此——不同终端窗口可以用不同版本互不干扰。理解了这一点很多玄学问题都能解释通了。4.2 默认版本和别名这两个配置能帮你省很多事默认版本是 nvm 最值得优先配置的东西。如果你不设置每次新开终端的时候nvm 会默认加载一个在nvm ls里标记为default的版本。如果从来没设置过一开始可能会提示node: command not found因为 nvm 装了版本但没切。设置默认版本的命令是nvm alias default 18.20.4设置完之后任何新开的终端窗口都会自动使用这个版本的 Node。这比每次手动nvm use方便太多了。别名功能更适合那种“你的项目长期固定在一个老版本”的场景。比如我手里有一个老项目永远跑在 Node 12 上我就给它设了一个别名nvm alias legacy 12.22.12之后切换的时候就只需要一句nvm use legacy不用每次去记那一长串版本号。这在你需要同时维护多个不同版本要求的项目时特别有用。4.3 用 .nvmrc 锁住项目 Node 版本团队协作不再翻车.nvmrc是 nvm 支持的一种项目级配置文件。你可以在项目的根目录创建这样一个文件里面只写一行版本号echo 16.20.2 .nvmrc然后在项目目录里执行nvm use注意这里不用带版本号。nvm 会读取当前目录下.nvmrc文件里的版本号自动切换。如果这个版本本地没装nvm 会提示你nvm install一下。更进一步nvm 还支持安装的时候自动读取nvm install如果你在项目目录下执行nvm install且存在.nvmrc它会自动安装文件里指定的版本。这个文件最大的价值在于团队协作。试想一下一个新同事入职拉下项目代码第一件事就是在项目目录敲一句nvm use版本立刻对齐根本不用在群里喊“谁告诉我 Node 用的哪个版本”。特别是后端项目命令行执行环境不同导致的行为差异非常隐蔽用.nvmrc可以提前规避。4.4 与 npm 全局包、yarn/pnpm 的配合技巧很多人切换 Node 版本后会发现哎我全局装的一些命令行工具比如vue/cli、create-react-app不见了。这是正常现象因为每个 Node 版本有自己独立的全局模块目录。我的建议是确定你的“主力版本”之后在这个版本里装一份常用全局包即可。比如nvm use 18.20.4 npm install -g yarn pnpm vue/cli这样切到其他版本时全局包确实没了但没关系——回到 18.20.4 就有了。这也是一种正向约束它逼着你把“全局工具”和某个明确的 Node 版本绑定而不是散落在一个不明不白的系统目录里。如果你使用 pnpm有一点特别提醒pnpm 的全局安装路径也可能受 Node 版本影响切换 Node 版本后如果发现 pnpm 命令失效不要慌重新执行一次npm install -g pnpm就行。此外如果项目的package.json里有packageManager字段比如packageManager: pnpm8.15.4建议用corepack来管理 pnpm 版本这样可以进一步细化到包管理器本身的版本控制。corepack是 Node 官方集成的一个工具会在后续版本里越来越重要值得提前了解。5. 实战踩坑与问题排查实录5.1 shell 切换后 nvm 失效怎么办有一种情况非常常见用户本来用 zsh后来因为某些工具要求换成了 bash一执行nvm就提示command not found。原因很简单nvm 的加载代码写在~/.zshrc里而 bash 只读取~/.bash_profile或~/.bashrc。解决办法就是把加载代码复制到对应 shell 的配置文件中。我个人的做法是在~/.zshrc和~/.bash_profile里都写上 nvm 加载代码。这样无论哪个 shell 被启动nvm 都能正常使用。如果你用的是 fish 或者其他的 shell官方文档也有对应说明逻辑不变跟着文档走就行。5.2 “cannot find module”类报错的排查思路热搜词里有一个很有代表性的报错node:internal/modules/cjs/loader:1568 throw err; Error: Cannot find module xxx这种报错看起来吓人其实绝大多数情况下就是两个原因一是当前目录下node_modules没装全二是 Node 版本不对导致依赖的原生模块编译产物不兼容。排查步骤我建议按这个顺序来第一确认一下当前生效的 Node 版本是否为项目要求版本。执行nvm current再对比.nvmrc或者package.json的engines字段。第二如果版本不对切换到正确版本nvm use。第三如果版本正确但还是报错删掉node_modules和锁文件重新安装一次。这一步能解决大多数残留模块导致的诡异问题。具体操作rm -rf node_modules rm -rf package-lock.json npm install如果是原生模块比如node-sass、sharp、bcrypt这类切换 Node 版本后必须重新安装因为它们编译出来的.node二进制文件是跟 Node ABI 版本绑定的。记住一个公式Node 大版本变了原生模块必须重装没有任何捷径。5.3 如何避免“同时存在两份 Node”的干扰还有一种不太容易察觉的问题系统里同时存在 Homebrew 装的 Node 和 nvm 装的 Nodewhich node的结果一会儿指向/opt/homebrew/bin/node一会儿指向~/.nvm/versions/node/...。造成这种混乱的原因通常是路径顺序问题。~/.zshrc里手动配置过 Homebrew 的路径而 nvm 的加载代码也写进去了谁的加载顺序靠前谁就先被找到。解决办法很朴素把 nvm 的加载代码放到 shell 配置文件的最后。因为 nvm 每次被加载时会把$NVM_DIR相关的路径插入到PATH的最前面这样node命令优先命中 nvm 管理的版本。如果你的配置文件里有什么自定义的export PATH...写在 nvm 加载代码之后就可能覆盖这个顺序需要留意一下。5.4 安装 Node 太慢或卡住的加速技巧nvm install 18.20.4这个命令默认从 Node 官网下载二进制包。在国内网络环境下下载速度可能非常慢甚至直接卡住不动。解决方法是给 nvm 配置镜像源。在~/.bash_profile或~/.zshrc里加一行export NVM_NODEJS_ORG_MIRRORhttps://npmmirror.com/mirrors/node/然后重新加载配置source ~/.zshrc之后再执行nvm install 18.20.4速度会有质的提升。这是 nvm 官方支持的环境变量不用担心兼容性问题。同理npm 的下载源也可以换成镜像源npm config set registry https://registry.npmmirror.com5.5 常见问题速查表问题现象常见原因解决办法nvm: command not foundshell 配置文件缺失 nvm 加载代码将 nvm 加载代码添加到~/.zshrc或~/.bash_profile后重新加载新终端node -v提示不存在没有设置默认版本执行nvm alias default 版本切换 Node 版本后全局包丢失每个版本全局目录独立在新版本上重装全局包Cannot find modulenode_modules 未安装或版本不匹配确认版本、重装依赖原生模块编译报错Node 大版本变化二进制不兼容删除 node_modules 后重新安装EACCES: permission denied之前用 sudo 安装过全局包用 nvm 统一管理避免使用 sudonpm 安装依赖太慢默认源访问受限配置 npmmirror 镜像源nvm install卡住不动官方下载源不稳定设置NVM_NODEJS_ORG_MIRROR环境变量5.6 几个我用下来特别顺手的操作习惯最后分享几个我个人养成的操作习惯不一定适合所有人但可以给你一些参考。第一个习惯每个新项目 clone 下来第一件事看有没有.nvmrc有就执行nvm use没有就根据package.json的 engines 字段创建.nvmrc并提交到代码仓库。这个习惯坚持下来你会发现团队里所有成员在本地的 Node 环境都出奇地一致。第二个习惯尽量只保留 2 个本地 Node 版本。一个是最新的 LTS用于大多数项目一个是项目指定的老版本用于兼容旧项目。不要看到新版本就装装得多了nvm ls列表会非常长反而增加心智负担。而且 LTS 版本足够稳定API 和生态兼容性都有保障其他非 LTS 版本更适合尝鲜或者特定调试不适合作为主力。第三个习惯全局包能少装就少装。能用npx临时执行的比如npx create-react-app my-app就不全局装。这样切换 Node 版本的时候需要重装的全局包就少迁移成本也低。全局配置只保留 pnpm、yarn 这些高频必备工具就够了。最后一个习惯定期更新 nvm 本身。nvm 的更新也是走 git 的进入~/.nvm目录执行git pull就行或者直接重新跑一次官方安装脚本。新版 nvm 会修复一些老的 node 版本的兼容问题也会支持更新的 Node 版本值得保持同步。6. 这块内容后续还能怎么扩展如果你已经熟练掌握了 nvm 的基本用法下一步我建议你了解一下corepack和 Docker 这两条线。corepack是 Node 官方自带的包管理器版本管理工具它能根据项目里的packageManager字段自动启用对应版本的 pnpm 或 yarn。它解决的是“不同项目用不同包管理器版本”的问题跟 nvm 解决的“不同项目用不同 Node 版本”的问题互补。我现在新起的项目基本是 nvm 管 Node 版本、corepack 管包管理器版本两者各司其职很少再遇到环境相关的幺蛾子。Docker 这条线则更进阶一点用node:18-alpine这类官方镜像作为开发容器把整个开发环境装进容器里。这种做法在团队协作中的优势非常明显——本地环境和 CI 环境完全一致再也没有“在我电脑上明明是好的”这种甩锅局面。但容器的学习曲线比 nvm 陡不少而且 Mac 上跑 Docker 本身要依赖虚拟机层资源占用也比较可观所以更适合有一定经验之后再上。我个人在实际操作中的体会是nvm 是整个前端/Node 工程化链路里性价比最高的一环花半小时装好配置好换来的是之后很长一段时间内不用再被版本问题打断思路。如果你正在 Mac 上被各种 Node 报错折磨不妨从今天这篇文章里的第一步开始把环境完整梳理一遍跑通一个版本切来切去的流程。那种各个项目一键对齐版本、新终端开箱即用的感觉用上之后你就知道有多香了。
返回列表