还记得第一次在终端里打开vim时的情形吗我盯着满屏的英文帮助文档按下方向键发现光标只能一格一格挪想往下翻页却卡在原地——那一瞬间我真觉得这个编辑器落伍到了极点。后来才明白之所以觉得vim的翻页命令难用是因为我们习惯了“鼠标滚轮PageDown”的连续滚动模式而vim的核心思维是“光标运动驱动视口变化”。这篇博客会把vim翻页命令拆开来讲逐行滚动、半屏滚动、整屏跳转、光标定位四个层次每个命令的使用场景、与普通编辑器之间的对应关系、以及实际项目里最快的那组操作组合都会给到。适合刚学vim的人也想给用过一段时间但始终记不住快捷键的朋友做一次系统梳理。1. 为什么vim翻页和普通编辑器完全不同——先理解光标与滚动的逻辑1.1 新手最常见的问题方向键失灵与页面静止很多人第一次进入vim后第一个动作就是按方向键移动光标第二个动作是尝试用PageUp/PageDown翻页。结果发现方向键能用但只能一格一格走想看下面几十行的代码就得按几十次下箭头PageUp/PageDown倒是能翻但翻一个屏幕之后光标到底在哪、自己刚才看到哪一行完全没概念。这其实不是vim“难用”而是它的交互逻辑和大多数编辑器不在一个频道上。在对齐了这套逻辑之后翻页操作就变得非常自然甚至比鼠标滚轮更高效。1.2 核心逻辑vim只有一种“光标”和一种“滚动视图”普通编辑器里滚动条拖动、鼠标滚轮、光标移动三者是绑定在一起的你按一下方向键页面跟着动光标也移动。vim把这件事拆开了光标位置你最终要编辑、要操作的那一行那一列。视口窗口终端里能看到的那个矩形区域。它就像相机镜头可以上下移动。所有翻页命令本质上只有两类一类是直接滚动视口让视野上下移动一类是移动光标让光标跳到另一个位置视口被动跟随。理解了这两类就不会再把命令记混。1.3 先记住一个朴素类比光标是“关注点”视口是“镜头”把vim编辑器想象成一个望远镜。光标是你要聚焦的景物视口就是镜头取景框。按j向下移动一行等于景物往下移镜头跟着转。按Ctrl-E等于景物不动只有镜头往下扫你能看到下面几行但关注点还在原处。按Ctrl-F等于你直接把镜头往下拉一大段连人带镜都换了个观察位置。这个类比几乎可以解释接下来所有翻页命令的行为差异尤其是“光标动不动”这个关键差异。先把这个想法刻在脑子里后面的命令表就是往这个框架里填具体按键。2. 翻页命令全景图逐行、半屏、整屏、重定位四类我习惯把vim的翻页命令分成四类逐行滚动、半屏滚动、整屏翻页、窗口重定位。下面这句话值得抄在笔记本上翻页命令虽然多但真正常用的只有七个Ctrl-E、Ctrl-Y、Ctrl-D、Ctrl-U、Ctrl-F、Ctrl-B、zz。2.1 逐行滚动Ctrl-E与Ctrl-Y和j/k的分工Ctrl-E视口向下滚动一行光标尽量保持不动。Ctrl-Y视口向上滚动一行光标尽量保持不动。这两条命令的作用场景比较特殊看长表达式时光标停在一行文字中间但想偷看下面几行的内容不想让光标离开当前行。更常见的场景是读日志文件日志一行接一行我想不打断当前阅读位置一点点往下“扫纸”这时候Ctrl-E就很顺手。不过如果光标随着滚动被推到了视口边界之外vim会强制移动光标把它保持在可见区域内。注意我上面说“尽量保持不动”就是指这种情况。对比一下j和k它们移动的是光标本身。当光标到达视口底部时再按j视口会被迫滚动一行让光标始终保持在屏幕内。这里就出现了一个很多人都会疑惑的点j是不是也是一种翻页从效果上说当光标碰到屏幕边缘时它确实会带动页面滚动但它是“先移动光标再带动页面”Ctrl-E则是“先移动页面尽量不管光标”。区分清楚这一点你就能解释为什么有时候按了很多次j屏幕却纹丝不动——因为光标还没走到屏幕边缘。2.2 半屏滚动Ctrl-D与Ctrl-U阅读节奏最舒服的一档Ctrl-D光标向下移动半屏视口同步下移默认滚动大约半个窗口的高度。Ctrl-U光标向上移动半屏视口同步上移。Ctrl-D命名的意思是“Down”Ctrl-U命名的意思是“Up”。这两条命令是我日常工作里使用频率最高的翻页命令原因是它兼顾了速度和上下文记忆。整屏翻页Ctrl-F容易让人“翻过头”因为一次跳得太远你往往会忘记刚才看到哪儿。逐行滚动又太慢。半屏滚动则刚刚好一次滚半页上一页的底部还能留在视野边缘形成视觉上的连续性阅读代码和长文档时不会产生割裂感。注意一个细节Ctrl-D和Ctrl-U默认滚动行数不是固定值而是受scroll选项控制默认值为窗口高度的一半。你可以通过:set scroll10把它改成固定10行改成之后这两条命令就变成了“固定行数滚动”。实测下来我不建议改半屏这个默认节奏是最好用的改成10行反而会让自己在一页一页翻的时候数不清已经看了多少。2.3 整屏翻页Ctrl-F、Ctrl-B以及真正的“重定位三兄弟”zz、zt、zbCtrl-F向下翻一屏对应普通编辑器的PageDown。Ctrl-B向上翻一屏对应普通编辑器的PageUp。F表示ForwardB表示Backward。这个命名逻辑非常简单“向前”是向下“向后”是向上和许多阅读软件把“下一页”叫“下一页”的习惯一致。翻整页时vim会保留一点上下文往下翻时上一屏的最后两行会留在新屏幕顶部往上翻时下一屏的开头两行会留在新屏幕底部。这个设计非常贴心至少不会让你在快速翻页时彻底丢失“刚才看到哪一行”的锚点。接下来是重定位三兄弟它们不是严格意义上的翻页但任何一个熟练用户都会把它们当作翻页体系的一部分zz把光标所在行移动到屏幕正中间。阅读时光标跑到屏幕底部了想让它回到视觉中心按一下zz整个世界都舒坦了。zt把光标所在行移动到屏幕顶部。看代码时想去掉上方无关区域就用这个。zb把光标所在行移动到屏幕底部。打个比方Ctrl-F和Ctrl-B像是把镜头直接拉走而zz、zt、zb像是把关注点“钉”住然后调整镜头让这个点出现在指定位置。实际使用中gg跳到文件开头后我会习惯性按一次zz让第一行居中搜索完关键词光标落在搜索结果上也会按一次zz让命中行居中视觉上一下子就能锁定位置。2.4 一次记住所有命令的自制速查表为了照顾从普通编辑器迁移过来的人我整理了一张对照表把vim命令和熟悉的操作对应起来习惯操作等效vim命令本质向下滚一行滚轮下Ctrl-E滚动视口向上滚一行滚轮上Ctrl-Y滚动视口向下翻半页Ctrl-D移动光标视口向上翻半页Ctrl-U移动光标视口向下翻一页PageDownCtrl-F移动光标视口向上翻一页PageUpCtrl-B移动光标视口当前行移到屏幕中间zz重定位视口当前行移到屏幕顶部zt重定位视口当前行移到屏幕底部zb重定位视口3. 光标运动与页面滚动的联动H/M/L与gg/G的定位逻辑3.1 H/M/L不更远地“移动镜头”只把光标放到镜头边缘先看三个颇具迷惑性的命令H把光标移动到当前视口最顶部那行High。M把光标移动到当前视口最中间那行Middle。L把光标移动到当前视口最底部那行Low。很多新手看到H会以为是“回到文件开头”看到L会以为是“翻到最后”实际都是误读。H、M、L不改变视口不上下翻页只改变光标在视口内的相对位置。举一个非常实用的场景你在浏览一个函数时光标已经滑到屏幕底部接下来想从屏幕开头读起。按H光标会瞬间回到视口第一行不需要按几十次k。再配合zz按H把光标放到顶部按zz把这一行重置到屏幕中间整个阅读区域就被调整到了舒服的位置。3.2 gg/G/数字gg跨距离跳转视觉上“像翻页”但本质完全不同gg跳到文件第一行。G跳到文件最后一行。数字gg跳到指定行比如150gg直接跳到第150行。从视觉上看按下G后屏幕会一下子滚到文件底部很多人就把它们归为“翻页命令”。但逻辑上这是完全不同的动作Ctrl-F是一次性翻过一页G是直接从第1行飞到第1000行相当于把整个视口从一个坐标点平移到另一个坐标点。在长文件中数字gg的效率远高于连续按Ctrl-F。要跳到第500行你可以估算一下当前在第200行然后按三次Ctrl-F大约翻到500行附近也可以直接输入500gg一步到位。更快的定位方式是配合行号显示:set number之后看到左下角的行号输入对应数字加gg就实现了精确跳转。3.3 跳转之后如何“回到刚才的位置”这才是翻页操作里的隐藏技巧。vim 维护了一个“跳转列表”jump list所有比较大的位移操作比如gg、G、数字gg、甚至搜索跳转都会把跳转前的位置记录下来。Ctrl-O在跳转列表里后退一步回到跳转之前的光标位置。Ctrl-I在跳转列表里前进一步。这个功能在实际阅读中极其好用的场景是你在文件第800行看到了一个函数的调用处用G跳到文件末尾看完整定义看完之后按Ctrl-O光标立刻回到第800行的调用处。它比“记住行号再手动输800gg”快得多。建议所有刚接触vim的人把Ctrl-O和Ctrl-I当作翻页体系的一部分来记忆它们是“反向翻页”的高级形态防止你在频繁跳跃中迷路。4. 真实场景组合读日志、审代码、看长文档时怎么用翻页4.1 读日志和长文件Ctrl-F/Ctrl-B 搜索跳转的组合阅读日志类文件是我最常遇到的长文件场景几十万行的日志不可能用方向键一格格翻。我的操作流程通常是打开日志后先按G跳到文件末尾因为最新日志基本都在尾部。按Ctrl-F向上翻页不是的这里是从尾部往回看按Ctrl-B向上翻屏。遇到关键字按/关键字搜索n跳到下一个匹配项N跳到上一个匹配项。每定位到一个关键行按zz让它居中然后读取上下文。这里有一个顺序上的技巧先用G定位到文件末尾再用Ctrl-B往回翻比从文件头部用Ctrl-F一路向下快得多因为日志文件末尾才是有价值的部分。不要从头开始看几十万行日志那样你翻到一半就想放弃。搜索跳转后别忘了Ctrl-O可以回退。比如搜索找到了第5个匹配项想看前一个匹配项周围的上下文按N过去阅读看完按Ctrl-O回到刚才的位置。4.2 长文档阅读与代码定位Ctrl-D/U的半屏节奏刚才说半屏滚动是阅读节奏最舒服的一档这在阅读长文档时体现得最明显。假设你在看一份几百页的API文档从头开始一页页读。Ctrl-F会直接翻一整屏信息密度大容易读漏Ctrl-E一次一行又太慢手会累。折中的方案是Ctrl-D一次滚半屏上一屏的下半部分还在眼前即使没有刻意记忆大脑也会因为“似曾相识”而保持连贯性。看代码时这个节奏更适用。函数定义往往不会超过一屏用Ctrl-D往下扫等看到目标函数开头按zt把它钉在屏幕顶部再开始逐行读。这样你能在一个稳定的视觉框架内读完整个函数体减少视线来回移动的损耗。配合:set number和:set relativenumber翻页后你能立刻估算光标和目标行之间的距离。比如光标在第120行看到第150行有个函数要跳过去直接输入150gg即可。这里relativenumber显示的是相对行号但跨大距离跳转时绝对行号更靠得住所以我自己的配置是两者都开着普通模式显示相对行号方便j/k移动时数行数。4.3 代码审查场景diff窗口之间的两屏切换代码审查时经常要看两处代码的关系一处是变更处一处是下游调用点。此时我会把屏幕上下分两个窗口上方窗口看修改下方窗口看引用。两个窗口都打开后Ctrl-W w在两个窗口之间切换焦点。在上方窗口按Ctrl-F翻页下方窗口保持不动方便对照。想快速把另一个窗口的视口拉到自己当前光标所在行可以执行:windo normal! zz让所有窗口都把当前行居中。这个场景说明了翻页命令不仅限于单窗口每个窗口都有独立的视口状态。我审查一次较大的改动时往往是在上下窗口中各翻几页用Ctrl-U/Ctrl-D微调视口位置然后用zz把关键代码居中。操作顺序的肌肉记忆比单个命令本身更重要。5. 让翻页更顺手的配置scrolloff、鼠标、映射与几个设置项5.1 scrolloff防止翻页翻到光标贴边scrolloff这个选项控制的是“光标距离屏幕边缘的最小行数”。默认值是0所以当光标到达屏幕底部后再按j屏幕才滚动。一旦设置了scrolloff5vim会提前在光标距离屏幕边缘不足5行时就开始滚动你永远能在屏幕上看到光标前后至少5行的上下文。这个设置对翻页体验影响非常大。设置前翻页到屏幕底部时最后几行代码紧贴底部视觉上很不舒服设置后哪一行都离屏幕边缘有一定距离视觉重心稳定。我的建议是设置5到8之间太大会导致文件开头和结尾处总是滚动空间不足反而诡异。对应还有sidescrolloff控制水平方向的光标边缘距离。如果你处理过长行代码设置set sidescrolloff5光标水平滚动到右侧边缘时不会立刻贴边会保留5列缓冲。尤其是在set nowrap不自动折行的情况下这5列缓冲非常重要。5.2 鼠标滚轮与PageUp/PageDown键的适配很多人刚用vim时第一个受不了的地方是鼠标滚轮不能用。要让滚轮可用只需在vimrc里加一行set mousea开启后鼠标滚轮默认会映射为Ctrl-E/Ctrl-Y的滚动行为即一次滚动若干行。如果滚轮滚动速度太快或太慢可以进一步映射 滚轮向下滚动一次映射为半屏向下 nnoremap ScrollWheelDown C-D nnoremap ScrollWheelUp C-U这里我建议按需调整不要一开始就设置。先体验默认滚轮的逐行滚动节奏如果觉得阅读长文档太慢再改成半屏映射。滚轮速度的偏好其实没有标准答案适合自己的才是最好的。PageUp/PageDown 键在vim里默认是有作用的PageDown对应Ctrl-FPageUp对应Ctrl-B。但有些终端或键盘映射环境下会失效。如果你习惯用这两个键可以在vimrc里显式声明nnoremap PageDown C-F nnoremap PageUp C-B注意只映射普通模式即可插入模式下按PageDown可能触发的是输入特殊字符而插入模式下想临时翻页可以先按Ctrl-O进入临时普通模式再执行一次翻页命令比如Ctrl-O Ctrl-F。5.3 自定义映射与普通编辑器的操作习惯如果你是从其他编辑器迁移过来的可能更习惯一些组合键比如CtrlJ表示向下翻页。vim里这类映射非常好加 让 Alt方向键 翻半屏 nnoremap A-Down C-D nnoremap A-Up C-U但这里我有一个真实建议不要过度自定义翻页键。vim默认的Ctrl-D/U/F/B在几乎所有终端模拟器里都稳定可用换成自定义组合键后换了电脑、换了终端或者用了别人配置的vim肌肉记忆就乱了。自定义键只建议用在那些“明显比默认更好记”的场景上比如把PageUp/PageDown强行绑回传统习惯或者把滚轮速度调到自己习惯的档位而不是把所有命令都换一套名字。另外提一个容易被忽略的细节终端模拟器的滚动缓冲区可能会干扰vim内的滚动。在终端里按住Shift再滚鼠标滚轮通常滚动的是终端缓冲区而不是vim视口。如果你在某个终端里发现滚轮翻页不生效先检查是不是因为终端拦截了鼠标事件或者确认vim是否开启了set mousea。6. 别人不说的坑与我的实操心得6.1 三个常见误区误区一把Ctrl-D当成“向下翻到底”。Ctrl-D只是向下半屏不是到文件末尾。要到文件末尾应该用G。这个混淆我见过很多次有人按了几次Ctrl-D发现还没到底又按了几次最后直接怀疑命令失效。半屏就是半屏长文件里真想到底按G就行。误区二在插入模式里按Ctrl-F。插入模式下Ctrl-F是重新缩进当前行不是翻页。刚学vim时我无数次在插入模式里想下一页结果代码缩进突然变了一脸懵。记住翻页命令属于普通模式要用翻页先按Esc回到普通模式。误区三以为zz可以代替Ctrl-F。zz不翻页它把光标所在行居中。如果光标在屏幕中间按zz什么都没发生。它不是没用而是作用完全不同。6.2 我的理解以光标为中心记命令比死记快捷键更持久最后分享一下我个人实操中的经验和心得。翻页命令之所以让人头大是因为这些按键的排列看起来毫无规律。但如果按“光标动不动”来分记忆难度会陡然下降光标几乎不动仅滚动视口Ctrl-E/Ctrl-Y。光标跟着一起走半屏或整屏Ctrl-D/Ctrl-U/Ctrl-F/Ctrl-B。光标不动视口也不动只是把光标挪到视口特定位置H/M/L。光标移动视口跟着大跨度跳动gg/G/数字gg。光标不动视口针对光标所在行重新构图zz/zt/zb。实际使用中我很少只单独用某一条翻页命令都是组合使用。比如最常见的阅读循环是Ctrl-D读半屏看到关键内容按zz居中再读几行然后用Ctrl-U往回看半屏。这个节奏稳定、速度快、视线不乱。再比如从一个很长的函数体中间跳到函数开头我的习惯是先gg到文件头然后Ctrl-F一屏屏往下找一旦看到目标函数名附近的代码就按数字gg精确调到那一行最后zz居中。整个过程用到的命令不过五六个但组合起来的效率比在IDE里拖滚动条高很多。坦白说我现在还是会在某些编辑器里不自觉地按Ctrl-F翻页然后发现页面没反应才意识到自己不在vim里。这说明肌肉记忆一旦建立就很难消除而它的威力也恰恰在肌肉记忆里。与其坐在那里背命令表不如把今天这张表打印出来贴在显示器旁边强制自己在真实项目里用一周一周之后你就不需要再看任何速查表了。