Mac 开发环境管理最佳实践:Homebrew + fnm + corepack + uv
作为 CPS 专业的学生,你的 Mac 就是你最重要的生产工具。不同课程需要不同版本的 Node.js 和 Python,如果全局安装一个大版本,另一门课的项目可能直接跑不起来。这篇文章会带你用 四个工具 搭建一套干净、可复现、互不冲突的开发环境。
整体架构
先把全局图景放在脑海里,我们使用的工具链是这样的:
flowchart TB
subgraph 基础层
HB[Homebrew<br/>macOS 包管理器]
end
subgraph 运行时管理层
FNM[fnm<br/>Node.js 版本管理]
UV[uv<br/>Python 版本管理]
end
subgraph 包管理器层
CP[corepack<br/>pnpm/yarn 代理]
PIP[uv 内置<br/>pip/venv 替代]
end
subgraph 项目层
PJ1[项目 A<br/>Node 18 + Python 3.11]
PJ2[项目 B<br/>Node 20 + Python 3.12]
end
HB --> FNM
HB --> UV
FNM --> CP
CP --> PJ1
CP --> PJ2
FNM --> PJ1
FNM --> PJ2
UV --> PIP
PIP --> PJ1
PIP --> PJ2
核心理念:每一层只管一件事,版本切换在项目级别自动生效。
第一步:安装 Homebrew —— 一切的起点
Homebrew 是 macOS 事实上的包管理器,后面所有的工具都通过它安装。
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
安装完成后,根据终端提示把 Homebrew 加入 PATH。如果你用的是 zsh(macOS 默认):
echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zshrc
source ~/.zshrc
验证安装:
brew --version
类比:把 Homebrew 想象成你 Mac 上的「App Store 命令行版」。后面要装什么工具,一行命令搞定。
第二步:fnm —— 轻量级 Node.js 版本管理器
为什么不用 nvm?
nvm 是最老牌的 Node.js 版本管理器,但它有两个痛点:
- 慢:每次打开终端都要
source一大段 shell 脚本,明显拖慢启动速度。 - 纯 Shell 实现:不支持 Windows,团队协作时配置不统一。
fnm(Fast Node Manager)用 Rust 编写,启动速度快 40 倍以上,且支持跨平台。
安装
brew install fnm
然后在 ~/.zshrc 中添加初始化:
echo 'eval "$(fnm env --use-on-cd --shell zsh)"' >> ~/.zshrc
source ~/.zshrc
--use-on-cd是关键参数:当你cd进一个包含.node-version或.nvmrc的项目目录时,fnm 会自动切换到对应版本。
常用操作
# 安装最新的 LTS 版本
fnm install --lts
# 安装指定版本
fnm install 20
fnm install 18
# 查看已安装的版本
fnm list
# 切换版本
fnm use 20
# 设置默认版本
fnm default 20
# 在项目中固定 Node 版本(进入项目目录后执行)
echo "20" > .node-version
验证
node --version # 应该显示 v20.x.x
npm --version # 应该显示 10.x.x
第三步:corepack —— 零安装使用 pnpm
为什么用 pnpm 而不是 npm?
| 特性 | npm | pnpm |
|---|---|---|
| 磁盘占用 | 每个项目独立存储 | 全局内容寻址存储,硬链接共享 |
| 安装速度 | 较慢 | 显著更快 |
| 幽灵依赖 | 有(扁平化 node_modules) | 无(严格的依赖隔离) |
| Monorepo 支持 | 需要 workspaces | 原生支持 |
对 CPS 学生来说,pnpm 的严格依赖管理能帮你避免很多「在我电脑上能跑」的经典问题。
启用 corepack
Corepack 是 Node.js 官方的包管理器代理,不需要单独安装,它随 Node.js 一起发布。你只需要启用它:
corepack enable
这会在你的 PATH 中注册 pnpm、yarn 等命令的代理。当你运行 pnpm 时,corepack 会根据项目 package.json 中的 "packageManager" 字段自动下载并使用正确版本。
在项目中使用
# 使用 pnpm 初始化项目
pnpm init
# 固定 pnpm 版本(推荐,确保团队一致性)
corepack use pnpm@latest
# 这会在 package.json 中写入类似:
# "packageManager": "pnpm@9.15.1"
# 安装依赖
pnpm install
# 添加依赖
pnpm add express
pnpm add -D typescript
常用 pnpm 命令速查
pnpm add <pkg> # 添加依赖
pnpm add -D <pkg> # 添加开发依赖
pnpm run <script> # 运行脚本
pnpm exec <cmd> # 在项目上下文中执行命令
pnpm store prune # 清理未引用的缓存包
第四步:uv —— 极速 Python 环境管理
为什么不用 conda / pyenv?
- conda:重量级,base 环境容易污染,启动慢。
- pyenv:编译安装 Python,在 macOS 上经常遇到 OpenSSL 编译问题。
- uv:Astral 团队(Ruff 的创建者)出品,用 Rust 编写,安装 Python 快 10-100 倍,内置虚拟环境和包管理功能。
安装
brew install uv
管理 Python 版本
# 安装最新的 Python 3.12
uv python install 3.12
# 安装多个版本
uv python install 3.11 3.12 3.13
# 查看已安装的版本
uv python list --only-installed
# 查看可安装的版本
uv python list
为每个项目创建独立环境
# 进入项目目录
mkdir my-project && cd my-project
# 初始化项目(会创建 .python-version 和 pyproject.toml)
uv init
# 指定 Python 版本
uv python pin 3.12
# 创建虚拟环境并安装依赖
uv add flask
uv add --dev pytest
# 同步依赖(类似 pnpm install)
uv sync
关键概念:
uv python pin会在项目目录生成.python-version文件,确保任何人 clone 这个项目后运行uv sync都能获得完全一致的环境。
常用 uv 命令速查
uv init # 初始化新项目
uv add <pkg> # 添加依赖
uv add --dev <pkg> # 添加开发依赖
uv remove <pkg> # 移除依赖
uv sync # 同步环境(安装所有依赖)
uv run <cmd> # 在虚拟环境中运行命令
uv python pin <version> # 固定 Python 版本
uv python install <ver> # 安装 Python 版本
第五步:项目级工作流示范
假设你这学期有两门课,分别需要不同的技术栈:
flowchart LR
subgraph "Web 开发课"
A[.node-version: 20] --> B[package.json<br/>packageManager: pnpm@9]
A --> C[.python-version: 3.12]
end
subgraph "数据科学课"
D[.node-version: 18] --> E[package.json<br/>packageManager: pnpm@9]
D --> F[.python-version: 3.11]
end
Web 开发课项目
mkdir web-course-project && cd web-course-project
# Node 环境
echo "20" > .node-version
fnm install && fnm use
corepack enable
pnpm init
corepack use pnpm@latest
# Python 环境
uv init
uv python pin 3.12
uv add flask
数据科学课项目
mkdir data-course-project && cd data-course-project
# Node 环境
echo "18" > .node-version
fnm install && fnm use
# Python 环境
uv init
uv python pin 3.11
uv add numpy pandas matplotlib
当你 cd 切换项目目录时,fnm 和 uv 会自动切换到对应版本,无需任何手动操作。
常见问题
Q:fnm 和 corepack 会冲突吗?
不会。fnm 管理 Node.js 二进制文件,corepack 管理包管理器(pnpm/yarn)。它们分工明确,互不干扰。
Q:uv 能替代 pip 吗?
能。uv add 和 uv pip install 都可以安装包,但推荐使用 uv add,因为它会自动更新 pyproject.toml 和 uv.lock,保证可复现性。
Q:要不要把 node_modules 和 .venv 加入 .gitignore?
必须。这两个目录应该永远在 .gitignore 中:
node_modules/
.venv/
__pycache__/
依赖锁定文件(pnpm-lock.yaml、uv.lock)必须提交到 Git,它们是环境可复现的保证。
Q:Homebrew Cask 是什么?
brew install 安装命令行工具,brew install --cask 安装 GUI 应用(如 VS Code、Docker Desktop)。两者都通过 Homebrew 管理。
brew install --cask visual-studio-code
brew install --cask docker
总结
| 工具 | 管理什么 | 安装方式 | 版本固定文件 |
|---|---|---|---|
| Homebrew | 系统级 CLI 工具 | brew install |
Brewfile(可选) |
| fnm | Node.js 版本 | brew install fnm |
.node-version |
| corepack | pnpm/yarn 版本 | corepack enable |
package.json 中的 packageManager |
| uv | Python 版本 + 包 | brew install uv |
.python-version + uv.lock |
记住这个原则:每个项目都有自己的版本文件,工具根据文件自动切换,全局永远保持干净。这样无论你有多少门课、多少个项目,环境都不会打架。