说起来有点不好意思,我前两年一直以为自己的 MacBook Pro 是台“开终端都会卡”的机器。每次按下新建标签页的快捷键,总要盯着一个空空如也的窗口等上大半秒,提示符才姗姗来迟;要是哪天进了 node_modules 堆成山的仓库,那一条命令执行完,得再等好几拍,屏幕才打出下一个提示符。这种体验我忍了很久。直到某天我实在受不了,给 zsh 的启动时间测了一下,差点没把水喷在键盘上——光是启动一个空提示符,Oh My Zsh 就耗掉了接近 1 秒。
后来把提示符引擎换成 Starship,同样的机器、同样的目录,启动时间掉到了 100 毫秒出头,输入命令也再没出现过“按键没反应”的错觉。这篇文章就把我从 Oh My Zsh 迁到 Starship 的全过程、背后的原理,以及中间踩过的坑一次说清楚。如果你正被终端启动卡顿折磨,可以照着走一遍,整个过程大概半小时。
1. 每次开终端都要等两秒?问题大概率不在电脑
1.1 一个让开发者血压升高的场景
你可能会说:就慢个零点几秒,至于吗?说实话,单纯一次启动慢一点确实不至于,但它不是只慢一次,而是你每天都在开几十个终端窗口。更难受的是那种“提示符已经出现,但按键要过一会儿才响应”的状态——它会直接打断你的输入节奏,严重的时候你真会以为是电脑配置不行。
我观察到一个规律:用 Oh My Zsh 的用户里,十有八九都经历过“打字打到一半感觉不对劲”的时刻。为什么?因为终端提示符看似只是一个输入位置,实际上它背后承担了很多活:Git 分支、当前目录、Python 虚拟环境、上一条命令的退出码……Oh My Zsh 的默认主题 agnoster 为了把这些信息展示得漂漂亮亮,会在每次渲染时同步去执行 Git 命令,再把结果算出来。也就是说,你每执行完一条命令,终端都得先跑一轮“状态探测”,才有新的提示符可用。
如果你用的还是 iTerm2 加一堆插件,那这个延迟体感会更明显。很多人第一反应是换电脑、加内存、换 SSD,但实际上瓶颈根本不在硬件,而在 shell 启动时加载了太多东西。
1.2 先量化问题:给 zsh 启动时间计时
与其靠感觉判断,不如直接量化。我当时的操作很简单,在 macOS 自带的 bash 里执行:
bash复制time zsh -i -c exit
这个命令会启动一个交互式 zsh、立刻退出,并输出启动所花费的时间。-i 表示交互模式,-c exit 表示执行完就退出,这样量到的就是纯启动开销,不掺杂任何命令工作。
我连续测了几次,结果大致是这样:
| 环境 | 启动耗时 | 说明 |
|---|---|---|
| 纯 zsh(无配置) | 30-50ms | 基本等于 zsh 本体启动耗时 |
| Oh My Zsh 默认配置 | 400-600ms | 没装什么插件的情况下 |
| Oh My Zsh + 常用插件和主题 | 800ms-1.2s | agnoster 主题 + 插件 + 语法高亮 |
| Starship + 自动补全/高亮 | 100-200ms | 我迁移后的最终形态 |
注意这里的耗时是“打开交互式 shell”的时间,也就是你新开标签页到出现提示符之间那段时间。从这个表能看出来,Oh My Zsh 默认配置已经比纯 zsh 慢了十倍以上,加上插件和重主题之后更夸张。
1.3 为什么会慢这么多?
简单说:Oh My Zsh 不是“一个插件”,它是一整套框架。为了让用户少写配置,它把 lib 目录下的几十个脚本全部加载进来,然后遍历你配置的插件目录,把每个插件里的 .plugin.zsh 文件逐一 source 执行,最后再加载主题。这三步听着不复杂,但架不住每一步都是脚本解释执行,涉及大量的环境变量初始化、补全函数注册、别名定义和钩子函数注册。
更关键的是,这些操作是同步的。在它们全部跑完之前,你的提示符不会出现,你的输入也不会被响应。所以启动耗时直接等于这些脚本的累计执行时间,插件装得越多,启动越慢。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 拆开 Oh My Zsh 的启动脚本,它到底干了多少活
2.1 .zshrc 里的三行代码和背后的加载链
绝大多数人的 .zshrc 开头长这样:
bash复制export ZSH="$HOME/.oh-my-zsh"
ZSH_THEME="agnoster"
plugins=(git z zsh-autosuggestions zsh-syntax-highlighting)
source $ZSH/oh-my-zsh.sh
这短短几行看起来人畜无害,但 oh-my-zsh.sh 内部做的远比你想的多。它会先设置 fpath,把 $ZSH/functions 和插件目录下的函数路径加进去,然后遍历 $ZSH/lib 下的所有脚本并 source,包括处理历史记录、补全系统、目录栈
