
如果你手上正好有一台运行着 macOS 13.7.8 的 Mac想把它变成日常开发 Vue、React 或小程序前端工程的工作机最快的方式不是去翻几十个网页逐个安装而是先想清楚这份“前端环境”由哪些部分组成再用一段可控的脚本把它们一键串起来。这篇文章要说的就是一套我实际用过的 macOS 13.7.8 前端环境一键搭建方案从 Homebrew、Node.js、nvm、pnpm 到 Git 和 VS Code一次性装完并且尽量不出错。它解决的不只是“装软件”的问题更关键的是解决“装完之后一团乱”的问题。很多朋友的环境坑就坑在版本不对、路径冲突、全局命令找不到、brew 和 nvm 互相踩踏这些细节上。这篇文章适合三类人准备入手 Mac 做前端开发的新手、被同事的“环境问题”反复求助的开发者以及想在团队里把开发环境标准化的技术负责人。照着做你拿到的不只是命令而是一套能落地、能复制、能排查的方案。1. 拿到新 Mac 后前端环境为什么必须“一键装”1.1 一份前端开发环境到底需要哪些东西先别急着执行命令我们把“前端环境”拆开看。前端开发不再只是写 HTML/CSS/JS真正跑起来需要一套工具链支撑。一份最常见的 macOS 前端环境长这样包管理器负责安装系统级命令行工具Node.js 负责运行 JavaScript 代码和前端构建工具版本管理器负责切换不同项目的 Node 版本包依赖管理器负责安装 npm 包Git 负责版本管理编辑器负责写代码。我建议的最小清单如下类别工具作用推荐安装方式系统包管理器Homebrew安装 git、watchman 等命令行工具官方安装脚本或镜像脚本Node 版本管理nvm安装、切换多个 Node 版本nvm 官方 install.shNode.js 运行时Node.js LTS本地运行 JS/TS 和前端工具链通过 nvm 安装包依赖管理器pnpm安装项目依赖通过 npm/corepack 安装版本控制Git代码版本管理Xcode CLT 自带编辑器VS Code日常写代码Homebrew Cask 或官网 DMG终端增强iTerm2可选比默认终端更好用的分屏与配色Homebrew Cask基础工具jq / tree / ripgrep处理 JSON、看目录树、搜索代码Homebrew有人可能会问为什么不用官网下载的 Node.js pkg 安装包还要多绕一层 nvm因为我见过太多次这样的场景项目 A 需要 Node 16项目 B 已经切到 Node 20你用官网包装的是全局唯一版本切版本只能靠卸载重装。用 nvm 之后每个项目可以单独指定 Node 版本项目根目录放一个.nvmrc文件nvm use一键切换这才是真正能长期维护的状态。和较早期的前端环境不同现在一个干净环境里通常还会带corepack它是 Node 官方用来管理 pnpm/yarn 的工具。不过在 macOS 13.7.8 上直接从 npm 全局安装 pnpm 仍然是最直观、最少坑的做法后面我会讲为什么。1.2 我坚持用脚本而不是“照着教程点”的原因手动安装看起来最可控实际上最容易出问题。安装顺序是强依赖的先有命令行工具才能跑编译先有 Homebrewnvm 才会省心先有 nvmNode 版本才有退路。如果你照着网上教程一步步操作中间任何一步因为网络、权限、弹窗中断后面就会进入“找不到命令”的死循环。脚本化之后有三个实打实的好处。第一可重复执行。脚本里每一项安装都写成“缺了才装装过就跳过”不管你是新机器还是已经折腾过半天的机器都能跑。第二可审计可交接。新人入职时把脚本丢给他跑完自己看日志哪里失败一目了然比口头教“你打开这个网站点下一步”靠谱得多。第三环境配置能进版本库。脚本放在 Git 仓库里以后重装系统、换电脑不需要依赖记忆。当然也不要指望脚本能自动化一切。Apple ID 登录、SSH Key 上传到代码平台、公司内部账号登录这类涉及身份认证的步骤脚本只能做到“提示到这里手动做”。我在实际写方案时会刻意把自动化边界划清楚凡是能静默执行的绝不打断凡是必须人工介入的最后统一列出清单这样跑完脚本后你不会漏掉任何一步。2. 动手前先摸底芯片、系统版本和终端2.1 Apple Silicon 还是 Intel一条命令确认架构在 macOS 上搭建前端环境第一件事不是装软件而是确定芯片架构。Apple SiliconM1/M2/M3 系列和 Intel 芯片对应的 Homebrew 安装路径完全不同这个差异会直接影响 PATH 配置和早期排障。打开终端执行uname -m如果输出arm64说明是 Apple Silicon如果输出x86_64说明是 Intel 芯片。Apple Silicon 上 Homebrew 默认装在/opt/homebrewIntel 上装在/usr/local。很多前端同学环境配好后终端一直提示command not found: brew多半就是安装了脚本里把/usr/local/bin写死而机器其实是 M 系列芯片。还有一个容易被忽略的点Apple Silicon 上通过 nvm 安装的 Node 默认是 arm64 版本绝大多数纯 JS 依赖没有问题但少数历史比较久的原生模块需要从源码编译。如果你遇到node-gyp编译失败先确认 Xcode Command Line Tools 是否安装再确认项目是否需要 x86 版本兜底。实在需要运行 x64 版 Node 时可以安装 Rosetta 2 后在 x86 终端里单独安装一套 nvm但不建议作为默认方案日常开发还是保持 arm64 原生最稳。softwareupdate --install-rosetta --agree-to-license2.2 系统版本和 Xcode Command Line Tools 是两条线系统版本用sw_vers查看输出ProductVersion: 13.7.8就代表当前系统。macOS 系统版本决定了很多工具链的兼容范围尤其是一些底层编译工具但比系统版本更需要关注的是 Xcode Command Line Tools简称 CLT。CLT 是一套单独的开发者命令行工具包含编译器、Git、make 等。前端开发绝大多数情况下不需要完整安装 Xcode因为它体积巨大、启动慢除非要打包 iOS 应用或做原生开发。CLT 才是日常必需品。检查是否已经安装xcode-select -p如果返回/Library/Developer/CommandLineTools说明已经装好。如果提示命令找不到就运行xcode-select --install系统会弹出安装窗口等待下载完成即可。安装完 CLT 后系统会自动提供 Git这也是为什么前面清单里说 Git 可以由 CLT 自带。不过系统自带 Git 版本通常偏保守如果你需要更新的 Git可以稍后通过 Homebrew 安装新版两者不冲突。我在给新同事配环境时会让他们先跑这三条命令uname -m、sw_vers、xcode-select -p。虽然看起来基础但很多时候环境问题根本不用排查到后面光是一个 CLT 没装就能让一堆包编译失败。2.3 shell 与 .zshrc很多环境问题的源头macOS 13 默认 shell 是 zsh配置文件是用户目录下的.zshrc。前端环境里 nvm、Homebrew、pnpm 的路径都是通过修改这个文件实现的。理解它的加载机制就理解了一半环境问题。先看当前 shellecho $SHELL正常情况下输出/bin/zsh。如果你之前手动切换过 bash 或其他 shell后面很多安装脚本默认往.zshrc写配置就可能不生效。.zshrc是 zsh 交互式启动时加载的配置也就是说每次新开一个终端标签页它都会重新执行一遍。nvm 就是靠往这个文件里追加几行加载脚本来生效。很多人装完 nvm 后发现当前终端窗口仍然提示找不到命令其实是因为当前窗口没有重新加载配置关掉重开或执行source ~/.zshrc即可。有一个非常值得提醒的点不要在.zshrc里无脑叠加export PATH...。我见过有人从网上复制各种配置后PATH 里出现四五份不同 Node 路径最终导致node指向的版本和npm指向的版本不是同一个。写脚本时我会统一让 nvm 管理 Node 路径避免和手动安装的 Node 冲突这是后续所有操作能顺利进行的前提。3. 一键脚本怎么设计才靠谱幂等、可重跑、留日志3.1 先给脚本画好骨架每样东西都按“缺了就装”来写写一键脚本最忌讳的是把安装命令从头到尾列一遍因为一旦中间某一步失败第二次运行时会从头再来可能把已经装好的东西又装一遍。好的安装脚本要做到“幂等”——同一个环境下执行一次和十次结果完全一致。我习惯把每一项安装都封装成一个ensure_xxx函数。函数内部第一件事是检查目标工具是否已存在如果存在直接跳过不存在才执行安装。比如 Homebrew 就检查command -v brewnvm 检查目录~/.nvm是否存在Node 检查nvm ls default是否有输出。这样脚本即使跑到一半失败修复问题后重跑也不会重复下载已经装好的东西。项目文件结构可以保持得非常简单~/mac-frontend-init/ ├── setup.sh └── logs/setup.sh是唯一入口logs/用来存放每次安装的输出日志。脚本不需要做成一个完整框架核心是流程清晰、失败可查。写脚本时我会固定 nvm 的具体版本。比如 nvmv0.39.7这样脚本在不同时间执行的结果是可复现的。如果你不固定版本三个月后官方把最新版改了个行为团队里新人和旧人的环境就又不一样了这就违背了一键搭建的初衷。3.2 核心安装函数拆解Homebrew、nvm、Node、pnpm先看一个我简化后的核心脚本结构实际运行时你不需要每行都懂但建议理解它做了什么。#!/usr/bin/env bash set -euo pipefail LOG_DIR$HOME/.frontend-init-logs mkdir -p $LOG_DIR exec (tee -a $LOG_DIR/setup.log) 21 ensure_xcode_clt() { if xcode-select -p /dev/null; then echo [OK] Xcode Command Line Tools 已安装 return fi echo [..] 安装 Xcode Command Line Tools请在弹窗中确认... xcode-select --install until xcode-select -p /dev/null; do sleep 5 done echo [OK] Xcode Command Line Tools 安装完成 } ensure_homebrew() { if command -v brew /dev/null; then echo [OK] Homebrew 已安装: $(brew --version | head -n1) return fi echo [..] 安装 Homebrew... /bin/bash -c $(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh) echo [OK] Homebrew 安装完成 } ensure_nvm() { if [ -d $HOME/.nvm ]; then echo [OK] nvm 已存在 return fi echo [..] 安装 nvm... curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash echo [OK] nvm 安装完成 } load_nvm() { export NVM_DIR$HOME/.nvm [ -s $NVM_DIR/nvm.sh ] . $NVM_DIR/nvm.sh } ensure_node() { load_nvm if nvm ls default /dev/null [ -n $(nvm ls default 2/dev/null) ]; then echo [OK] Node 默认版本已存在: $(node -v) return fi echo [..] 安装 Node.js LTS... nvm install --lts nvm alias default lts/* echo [OK] Node 默认版本: $(node -v) } ensure_pnpm() { if command -v pnpm /dev/null; then echo [OK] pnpm 已安装: $(pnpm -v) return fi echo [..] 安装 pnpm... npm install -g pnpm echo [OK] pnpm 安装完成 } main() { ensure_xcode_clt ensure_homebrew ensure_nvm ensure_node ensure_pnpm echo 全部完成建议执行: source ~/.zshrc } main这个脚本里有一个细节nvm ls default在 Node 未安装时返回非零状态但加上|| true或者放进 if 判断里会更稳妥。加上set -euo pipefail后脚本遇到未处理错误会直接退出这正好符合“失败了就停下别带病继续装”的思想。注意ensure_node中的写法先加载 nvm再判断默认版本是否存在。很多网上脚本会省略load_nvm直接调用nvm结果脚本运行时报command not found: nvm。原因是 nvm 本身是一个 shell 函数不是可执行文件需要在当前 shell 里 source 后才能用。3.3 日志、环境变量注入与失败断点重跑日志是一键脚本最容易忽略但实际最重要的功能。没有日志时安装失败了你只能重新跑一遍然后再凭记忆判断是卡在哪一步。加日志后每步的 stdout/stderr 都写入setup.log排障时直接搜索[..]或[OK]标记就能定位。exec (tee -a ...)这行把脚本输出同时送到屏幕和日志文件。运行完脚本后即使终端往上翻不到历史你也能在~/.frontend-init-logs/setup.log里看到完整过程。断点重跑依赖的是幂等设计。举个例子如果脚本在安装 pnpm 时因为网络问题失败你只需要修好网络重新跑前面的 CLT、Homebrew、nvm、Node 都会直接命中“已安装”分支只有 pnpm 会被重新安装。这比手动一步步找“到底装到哪了”要省心得多。环境变量注入这里我建议在脚本最后集中追加一份配置到~/.zshrc内容控制在最小集export NVM_DIR$HOME/.nvm [ -s $NVM_DIR/nvm.sh ] . $NVM_DIR/nvm.sh export HOMEBREW_NO_AUTO_UPDATE1HOMEBREW_NO_AUTO_UPDATE1很实用。不设置这个变量时每次执行brew install都可能先帮你更新 Homebrew 自身网络慢的时候会造成长时间卡住实际不更新也不影响安装软件。最小化.zshrc注入是防止环境崩掉的好习惯。很多人喜欢把网上整个美化配置复制过来最后 zsh 启动速度变慢还找不到原因。我倾向于先把基础路径写对编辑器主题、命令行提示符这些属于个人偏好等环境能正常跑项目了再慢慢折腾。4. 实操走一遍从空系统到能跑 Vue/React 项目4.1 安装 Homebrew顺便把源切到更快的镜像Homebrew 是 macOS 上前端工具链的地基很多命令要通过它安装。执行官方安装脚本最简单的写法是/bin/bash -c $(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)脚本执行过程中可能会要求输入密码这是因为它需要创建/opt/homebrew或/usr/local目录。等待时间取决于网络状况如果你发现下载很慢或者频繁中断我建议在安装前先给终端设置几个镜像源环境变量把默认仓库指到国内高校或云厂商的镜像站速度和稳定性都会好很多。以清华镜像源为例安装前先执行export HOMEBREW_BREW_GIT_REMOTEhttps://mirrors.tuna.tsinghua.edu.cn/git/homebrew/brew.git export HOMEBREW_CORE_GIT_REMOTEhttps://mirrors.tuna.tsinghua.edu.cn/git/homebrew/homebrew-core.git export HOMEBREW_BOTTLE_DOMAINhttps://mirrors.tuna.tsinghua.edu.cn/homebrew-bottles再运行安装脚本。安装完成后把这三个环境变量写入~/.zshrc以后每次 brew 安装都会自动走镜像。不同镜像站的具体配置路径可能会有更新使用前建议以镜像站官方文档为准。安装完成后检查是否成功brew --version同时确认 shell 能否找到 brew。如果你的芯片是 Apple Silicon 但系统提示找不到 brew检查 PATH 里有没有/opt/homebrew/bin。Apple Silicon 上 Homebrew 不会自动把路径写进所有 shell 的配置文件通常需要执行echo eval $(/opt/homebrew/bin/brew shellenv) ~/.zshrc然后重新加载配置。4.2 用 nvm 安装 Node.js LTS避免官方 pkg 的坑很多人第一次装 Node 喜欢去官网下载 pkg 安装包双击安装后直接在全局生效。这在临时体验时没问题但长期开发会带来两个麻烦一是卸载不干净二是不方便切换版本。nvm 把 Node 安装到用户目录下想换版本只需要nvm install和nvm use不会污染系统目录。安装 nvm 的官方命令是curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash如果网络不稳定建议先把 install.sh 用浏览器或镜像方式下载到本地再执行bash install.sh。安装输出会提示脚本已经把内容写入到了~/.zshrc所以新的终端窗口可以直接用 nvm。安装 Node LTS 并把它设为默认版本nvm install --lts nvm alias default lts/*nvm alias default的作用是给“当前最新的 LTS 版本”设置别名避免以后手动切换后默认版本漂移。设置完成后每次新开终端Node 都会自动指向默认版本。我强烈建议新建项目时在项目根目录放一个.nvmrc文件内容写20或18这种主版本号。这样其他成员进入项目后执行nvm usenvm 会读取文件并自动切换到对应版本。前端项目依赖锁文件是很常见的但很多人会忽略 Node 版本的一致性结果 lockfile 相同、跑出来的结果却不同.nvmrc能很大程度避免这种问题。4.3 pnpm 与常用前端 CLI按需装别贪多Node 装好后npm 会随 Node 一起提供。如果你不打算深度使用 pnpmnpm 其实够用。但我在实际项目中对 pnpm 的体验更好安装依赖速度快磁盘占用低对依赖提升问题管理更严格。这里有一个经验团队如果已经统一使用 npm 或 yarn尽量别在一台机器上强制切换 pnpm先看项目