zsh 的交互式体验堪称是最强的——丰富的插件,强大的框架,将 zsh 的交互式体验推向了极致。然而另一方面,过多的插件,臃肿的主题,也让 zsh 变得反应迟钝,反过来破坏了交互式体验。
很多人反映的 zsh 的慢主要体现在两个方面:1. 启动速度,2. prompt 反应速度慢
影响第一点的主要是插件的数量和质量,影响第二点的则是主题。 因此不少人想到的第一个解决方案就是精简插件和主题。诚然,这是最有效的解决方案,但是这就和使用 zsh 的初衷相违背了:使用 zsh,不就是希望自己的 shell 能够用起来更方便么?
而且很不巧的是,zsh 社区最为著名的框架——oh my zsh(下称 OMZ),针对这两点都没有任何优化。不仅给 zsh 带来了龟速的名声,还让不少人由 OMZ 入门的用户最终都抛弃了 OMZ,转而使用插件管理器自行创建配置(也有人创建了在速度上进行了优化的框架如 Prezto、ZIM)。
然而这并不能解决问题。说到 zsh 的插件管理器,最著名的就是 antigen,可以说是 zsh 社区的事实标准。可是这个事实标在速度方面也没有进行过多优化,因此在插件慢慢变多以后,还是会发现 zsh 的启动时间变得越来越慢……
于是一大批插件管理器接连冒出,antibody,zgen,zplug……每个都宣称自己解决了问题,然而都不尽如人意。毕竟能采用的优化方式无非就是提前编译+缓存,再怎么优化都是有极限的。直到我发现了 zinit……(PS:zplug 的思路其实也很棒,但是实现实在是太挫了,反倒起了反作用)
注:zinit 原名 zplugin,写这篇文章的时候还没有改名,所以图片里都是 zplugin。
第一眼看到 zinit 的时候,其实我是拒绝的,因为它的 GitHub repo 只有数百 star,我下意识觉得这不过又是一个没啥新意的轮子。直到我看到这篇文章,比较了各大 zsh 框架&插件管理器的速度,我才愕然发现,最不起眼的 zinit,竟然是最强悍的一个!
看到这幅图,不少人肯定会觉得——这肯定是作弊了吧?怎么可能快?
我当初也是怎么想的,然而试用了以后,我发现——竟然真的这么快!而且插件数量越多 zinit 优势越大!
zinit:我不是针对谁,我是说,除我以外,在座的各位,都是辣鸡!
为什么 zinit 会这么快?即使是预编译了插件,对外部命令的调用却是优化不了的(这也是拖慢启动速度的大头),这个速度和其他插件管理器比起来,简直就是降维打击!
秘密就在于它提供的一个独特的插件加载机制。
众所周知,尽管不少人会给 zsh 配上一堆插件,但很多插件都不是启动一个 shell 以后立马就需要使用的。 比如说 thefuck,这个插件只有在你打错的命令的时候才会被激活。然而为此你却需要在启动时花费数百毫秒来加载,这显然是一种浪费,为什么不能将这类插件的加载延迟到 zsh 启动以后呢?
zinit 就是在这个方向上的一次成功尝试。它提供了 “Turbo Mode”,允许延迟加载插件,一般来讲可以加速 50% 到 70%!
说了这么多,究竟该如何使用呢?
首先声明一下,我并不打算写一篇详尽的使用指南,因为我已经写过一篇。 然而回过头来看才发现这玩意儿实在是……又臭又长,里面讲的很多功能其实根本用不到……(尽管那篇文章声称自己只覆盖了 zinit 功能的冰山一角(没办法,zinit 功能实在是太多了))
因此这篇教程决定只覆盖最常用的命令,让人快速上手。如果看完文章以后你对 zinit 确实产生了兴趣,倒是可以阅读一下那篇文章。
刚正朴实的安装方式,官方推荐
sh -c "$(curl -fsSL https://raw.githubusercontent.com/zdharma/zinit/master/doc/install.sh)"
没有诸如需要使用 dotfiles 管理配置的需求的话,不建议手动安装
首先 clone repo 到随便哪个位置
git clone https://github.com/zdharma/zinit.git ~/.zinit/bin
然后在你的 ~/.zshrc 顶端添加如下语句
source ~/.zinit/bin/zinit.zsh
现在,就可以在你的 ~/.zshrc 中使用 zinit 命令来加载插件了~
最为常见的加载方式,和其他插件管理器的用法是一致的,直接 zinit light {用户名}/{repo名}
即可。
zinit light zsh-users/zsh-autosuggestions
zinit light zdharma/fast-syntax-highlighting
# PS. zinit 和 fast-syntax-highlighting 是同一个作者,这位作者对速度的追求确实让人钦佩
OMZ 别的不说,插件是真的多,如果不能加载 OMZ 插件那就太遗憾了。因此像 antigen 这样的插件管理器甚至直接提供了整合 OMZ 框架的能力。
zinit 并没有直接整合 OMZ,不过提供了更为强大的 snippet
命令来加载单独的插件。
snippet
命令可以接 URI 来直接加载单个文件。比如加载 OMZ 的 sudo 插件:
zinit snippet OMZ::plugins/sudo/sudo.plugin.zsh
后面那部分看起来好像不像一个 URI?因为 OMZ 实在太常用了,因此可以用 OMZ::
来代替它的 repo 地址(对于 Prezto 则是 PZT::
),上面的写法其实等价于:
zinit snippet https://github.com/ohmyzsh/ohmyzsh/blob/master/plugins/sudo/sudo.plugin.zsh
有时候,插件目录里不止一个文件(比如包含插件+补全),这时就需要使用 svn
修饰词来加载了:
zinit ice svn
zinit snippet OMZ::plugins/extract
这里不详细解释什么叫修饰词了,你可以理解成一种实现可选参数的手段,修饰词只对接下来的一条命令起效。 格式是 zinit ice {修饰词1} {修饰词2} ...
svn
修饰词,表示下一行的 URI 需要使用 SVN 协议加载。此时 zinit 会使用 SVN 协议下载整个目录,自动识别并加载需要的文件。
补全文件,有两种加载方式:1. 使用 svn
修饰词直接加载目录,zinit 会自动识别并加载补全。 2. 直接加载补全文件,此时需要使用 as="completion"
这个修饰词,它会让 zinit 将下一行命令加载的文件作为补全安装到指定目录:
zinit ice as="completion"
zinit snippet OMZ::plugins/cargo/_cargo
OMZ 的部分插件/主题会依赖 OMZ 本身提供的功能。比如 git 插件,如果想要正常使用的话,需要加载 OMZ 的 git 库。
# 加载 OMZ 的 git 库
zinit snippet OMZ::lib/git.zsh
# 加载 OMZ 的 git 插件
zinit snippet OMZ::plugins/git/git.plugin.zsh
也就是说,虽然 zinit 并没有提供直接加载 OMZ 框架的能力,但是你可以使用 snippet 功能选择性加载框架的部分功能。这比其他插件管理器的做法更为灵活。
比如我的配置里就直接加载了 OMZ 的部分库,如果你也是 OMZ 的用户,建议同样加载这些库,能保证体验一致。(比如键位绑定)
zinit snippet OMZ::lib/clipboard.zsh
zinit snippet OMZ::lib/completion.zsh
zinit snippet OMZ::lib/history.zsh
zinit snippet OMZ::lib/key-bindings.zsh
zinit snippet OMZ::lib/git.zsh
zinit snippet OMZ::lib/theme-and-appearance.zsh
终于说到 zinit 的最强 feature 了。没有 Turbo Mode 的 zinit 不过是略为优秀的插件管理器。然而有了 Turbo Mode,zinit 可以说是顶级 zsh 插件管理器,没有之一!
这个功能非常强大,基本用法却很简单。举例来说,下面的代码,就实现了 OMZ git 插件的延迟加载。
zinit ice lucid wait='0'
zinit snippet OMZ::plugins/git/git.plugin.zsh
延迟加载用到了两个修饰词:lucid
,用于静默加载。wait={秒}
,在 prompt 加载完毕后的若干秒后再加载。上面的例子就是让 prompt 启动后立刻加载 git 插件。
对于绝大多数插件来说,这两个参数已经足够了。但是有个别插件是例外,这样的例外很少,在这里就直接列出来了。
zinit ice lucid wait='0' atload='_zsh_autosuggest_start'
zinit light zsh-users/zsh-autosuggestions
zsh-autosuggestions 的延迟加载用到了 atload='_zsh_autosuggest_start'
修饰词,因为wait='0'
会让第一个 prompt 加载完成后再加载 zsh-autosuggestions,于是第一个 prompt 就无法使用 autosuggesstions,必须手动激活。
2. 补全类插件
比如 “zsh-users/zsh-completions”,延迟加载补全可能导致无法补全。修复手段当然是有的,但是延迟加载补全的意义并不大,因为本身并不耗时,建议不要延迟加载补全。
不过即使不延迟加载补全,可能也会发现补全用不了,而且有些自带补全的插件一旦延迟加载就会导致补全失效。
如果没有延迟加载与补全相关的插件,可以简单地在配置末尾添加 autoload compinit; compinit; zinit cdreplay -q
来手动初始化补全。
如果延迟加载了补全相关的插件就比较麻烦了,需要给最后一个补全相关的插件添加 atload="zpcompinit; zpcdreplay"
修饰, 相当于同时延迟初始化补全。
# 假设 git 插件是最后加载的
zinit ice lucid wait="0" atload="zpcompinit; zpcdreplay"
zinit snippet OMZ::plugins/git/git.plugin.zsh
3. 主题
主题的延迟加载是可行并且有意义的,可以起到 powerlevel10k 主题中 “Instant Prompt” 的效果。即先提供一个精简的 prompt,主题加载完毕后再切换到完整版。
越是重量级的主题,加速效果越是明显,然而同样配置起来越是复杂。事实上如果将其他耗时插件都延迟加载后,启动时间应该已经降到了 100ms 以下,不必延迟加载主题也已经够快了。
如果坚持追求极致速度的话,可以参考:zplugin_tutorial/#turbo-mode-加载复杂的命令提示符。
前面提到 zsh 速度慢主要有两个方面,一个是插件过多造成的启动速度慢,这个可以通过 zinit 解决。
然而另一个 prompt 反应过慢,就只能通过更换主题解决了。当然,并不是更换简陋的主题,而是更先进的异步主题!异步主题能够在后台进行耗时的 git 信息统计等操作,完成后再刷新 prompt,有效避免了进入大 repo 的卡顿。
首先 OMZ 内置主题全部排除!这些主题都不是异步主题,进入大 repo 时卡顿非常明显。
这里只推荐两个
我目前使用的主题,不过定制能力不强,我不得不修改源码以符合自己的使用习惯
powerlevel9k 的进化版本,速度和定制能力都很强的主题,但是瞅着有些复杂最终被我拒绝(嘛,也说不定哪天就真香了)
(更新:我已经真香了,强烈推荐这个主题,自带的 Instant Prompt 功能可以让你的 zsh 启动瞬间就进入可用状态)
直接 Copy & Paste