寫給 VS Code 用戶的 Vim 入坑指南

現實當中使用 Vim 來寫前端的人是少之又少,大多數人基本上都是使用 VSCode。但作爲「編輯器之神」,不管使不使用 Vim 進行編碼,學習 Vim 的編輯模式都是有好處的。

本文會從最基本的使用和配置開始介紹作爲前端工程師怎麼從零到一到開始使用 Vim。當然,我也不期望因爲一篇文章而讓廣大的 VSCode 用戶轉用 Vim,所以最後會介紹如何在 VSCode 中使用 Vim 插件進行更高效的編碼。

基本使用

模式

與一般的編輯器最大的不同是,在 Vim 中有 4 種編輯模式,分別是:普通模式、插入模式、可視模式和命令行模式。使用 Vim 進行編輯就需要熟練的在各個模式之間進行切換。

四種編輯模式

普通模式

一般情況下每次進入編輯器的時候默認爲普通模式,此時可以使用 h/j/k/l 來對光標進行移動。

普通模式下我們的輸入會被當作是一個命令,而命令是可以指定次數的。例如我們想要向下移動 10 行。我們可以在普通模式下連續按 10 次 j(類似於在普通編輯器中連續按 10 次向下鍵),除此之外我們也可以直接輸入 10j。

移動光標

在 Vim 中爲了提高輸入效率而區分編輯模式,因此我們可以在儘量少的移動手指的情況下來進行光標的移動。以下是一些在普通模式下常用的用於移動光標的命令:

h/j/k/l 等同於一般情況下的上下左右鍵,能夠在不移出鍵盤主區域的情況下移動光標。一種簡單的記憶方式是左右兩邊的 h/l 代表左右移動,而食指在鍵盤的默認位置 j即爲向下,反之 k 爲向上。

如果你也是快捷鍵小能手的話,大概率也會使用 ctrl + a 或者 ctrl + e 來跳到行首或者行尾。 在 Vim 的普通模式下我們可以使用 ^/$ 來進行這個操作,如果你熟悉正則表達式的話可以發現其實 ^ 就是表示行首的意思,而 $ 是行尾的意思。

我們這個時候已經能夠上下左右移動光標和跳轉到行首行尾了。你可能會問,如果我只想移動兩個單詞的位置的話該怎麼辦?這時候我們就可以使用 w/b 來移動到下一個單詞或者上一個單詞的單詞頭了。如上所提到的,在普通模式下命令是可以指定字數的,所以我們可以通過 2w 來移動兩個單詞的位置。

使用 ^/$ 與 w/b 快速移動光標

這時候又有了另外的一個疑惑,我們在移動光標的時候總不能還在腦子裏面算一下還有幾個單詞的位置吧?就算連續的按 w 來到達我們想要到的地方也有點蠢,所以在 Vim 中,我們還能夠使用 f{char}/F{char} 來向前 / 向後搜索某個字符,然後跳過去。

因此,我們也可以通過 f{char}/F{char} 來操作光標跳轉到上圖的位置:

使用 f{char}/F{char} 跳轉到指定字符

通過以上的各種跳轉方式可以滿足絕大多數跳轉的場景,熟練使用之後基本上就不要再通過鼠標去移動光標了。

插入模式

插入模式其實大家都很熟悉,一般的編輯器默認的狀態就是 Vim 中的插入模式。在插入模式我們能夠進行插入字符、換行等操作。

當然,在這個模式裏面也可以通過方向鍵移動光標,假如忽略其他的模式的話,完全可以把 Vim 當成普通的編輯器進行使用。但這樣的話就失去了學 Vim 的意義了,Vim 最值得學習的就是通過切換編輯模式來進行快速的移動 / 輸入 / 選擇。

Vim 實用技巧 中是這樣描述 Vim 中的普通模式的:

畫家在休息時不會把畫筆放在畫布上。對 Vim 而言也是這樣,普通模式就是 Vim 的自然放鬆狀態,其名字已經寓示了這一點。

誰說一定要切換到插入模式纔行?我們可以重新調整已有代碼的格式,複製它們,移動其位置,或是刪除它們。在普通模式中,我們有衆多的工具可以利用。

所以當我們使用 Vim 的時候,只有想要輸入點什麼內容的時候纔會進入插入模式。那麼我們如何從普通模式進入插入模式呢?

從普通模式進入插入模式有許多中方式:

進入插入模式

變種命令

除此之外,我們還可以使用變種的 I/A/O 來使用不同的插入姿勢。Ii 都是在前面進行插入,而不同的是 I 是在行首進行插入,操作有點類似與使用 ^ 將光標移動到行首之後在通過 i 進入插入模式。相似的 A 是在行尾進行輸入,約等於 $a

使用o在光標所在行的後面插入新一行進行插入,那麼如何在光標所在行的前面插入新一行呢?我們可以使用 O 來做類似 k 向上移一行然後 o 的操作。

簡單來說,可以這樣去記憶:

那麼如何在插入模式中切換到普通模式呢?答案是 Esc 或者 ctrl + [。我們在任何模式當中都可以通過這兩種方式來切換回普通模式。所以一般的流程是普通模式中移動光標或者執行命令,然後在需要插入代碼的位置切換到插入模式進行編輯,編輯完成之後再切換回普通模式。就像是畫家思考完成之後拿起畫布畫,然後再放下畫筆繼續思考,以此往復。

如果你能夠練習到熟練的切換普通模式與插入模式的話,會發現在輸入效率上會有所提升。雖然這可能需要經歷一段陣痛期,一但形成肌肉記憶之後就可以不需要回憶那個命令是自己想要的操作了。這一點上跟從全拼輸入法切換到雙拼輸入法的時候的感覺類似。習慣了通過切換模式 + 命令的方式移動光標進行輸入後,移動鼠標去點擊的動作就會顯得非常的不自然。

可視模式

細心的你可能會發現,到目前爲止我們只提到了如何在普通模式下進入插入模式進行編輯操作。沒有提到如何在普通模式下面進行刪除操作。

雖然你也可以在插入模式裏通過刪除鍵進行刪除,但此時只能夠刪除光標前的字符。顯然,我們總不能每次都切換到普通模式後移動光標,再進入插入模式進行刪除操作。插入模式,最大的職責就是進行字符的插入。

所以,我們可以在普通模式中移動光標,然後通過 x 這個命令來刪除對應的字符,並且通過 d 來刪除指定範圍的內容。

以下是命令 d 的常用用法:

一般情況下我們會使用 dd 來刪除一整行,但是真實的情況是我們可能不想一整行都刪除,這時候你可以會想要進入插入模式使用鼠標選中某一點要刪除的內容然後按刪除鍵。這有笨拙,我們有更方便的做法。

在普通模式裏面我們可以使用 v 進入可視模式,在可視模式中允許我們通過普通模式中移動光標的方式來選中某一段內容進行操作。使用的 v 命令是進入以字符爲單位的可視模式,此外我們還有以行爲單位的可視模式的命令 V 以及以列爲單位的可視模式命令 ctrl + v。這裏我們主要討論 以字符爲單位的可視模式 v,另外可視模式的區別請自行了解。

剪切、複製與粘貼

當我們在普通模式中按 v 的時候,會把當前光標爲主作爲選區的起點,然後我們可以通過 h/j/k/l/w/b 等進行光標的移動,確定選區的終點。

在可視模式中進行選擇

在選擇了要操作的範圍之後我們就可以使用 d 命令來進行刪除。使用 d 命令進行操作的時候實際上進行的是我們熟悉的「剪切」操作,我們可以通過另外的一個命令 p 在普通模式下進行粘貼操作。

d 命令類似的還有複製命令 y,我們可以通過 yy 來複制一行內容。同樣的,也可以進入可視模式之後選定指定的內容進行復制,然後回到普通模式進行 p 粘貼操作。

文本對象

講到這裏就不得不說 Vim 非常有特色的文本對象(text object)了。文本對象能夠讓我們不移動光標的情況下來操作一定區域內的內容。

上面提到的 diw 刪除當前光標所在的單詞的命令中就使用到了文本對象,我們可以把這一整個命令拆解爲 d + iw。這裏的 iw 即爲文本對象,指的是當前單詞的意思。

我常用的其他文本對象:

其中,i(a( 中的括號可以換成任何成對的符號,例如 i[i{i" 等,表示刪除成對的符號範圍的內容。

既然 d 命令後面可以跟着文本對象,那麼同理 y 命令進行復制的時候也是可以跟着文本對象來指定複製的區域內容的。例如我們可以在普通模式下通過yiw來複制光標下的單詞:

複製文本對象

類似的,我們還能夠快速的複製任何成對符號中的內容,這在複製代碼中的字符串時非常的有用。通過文本對象我們可以無需進入可視模式而進行一些簡單的操作。關於文本對象的內容非常的多,這裏由於篇幅原因就不詳細介紹了,有興趣的小夥伴可以自行去了解。

命令模式

在普通模式中我們輸入 : 會進入命令模式,在命令模式中我們可以使用與 shell 下的命令行類似的命令,比如我們退出 Vim 編輯器需要使用的 :qa 命令。

這裏指介紹下我最常使用的命令:

命令模式下支持非常多的命令,但一般來說我們不需要進入命令模式就可以完成我們的編輯操作了,並且把需要經常使用的命令模式下的命令映射到某一個快捷鍵當中來進行使用。

快捷鍵

例如我們可以把上面的字符串的命令映射到某一個快捷鍵上,在 ~/.vimrc 文件中添加如下內容:

然後在命令模式中重新載入我們的 vim 配置::source $MYVIMRC。之後我們就可以通過 ctrl + f 這個快捷鍵來進入命令模式,並且自動的爲你輸入 :s/,我們只需要在後面輸入對應的替換的正則表達式就可以了。

這裏的 nnoremap 表示的意思是普通模式下的非遞歸映射,此外我們還有 inoremapvnoremap 等表示在不同模式下的非遞歸映射。

所以到底什麼是非遞歸映射(noremap)?這裏我們需要先介紹下遞歸映射,如果我們有如下的配置:

那麼就相對於是 map c b,即快捷鍵的映射會被其他的映射所影響。而如果是非遞歸映射的話:

當我們按下 c 的時候會直接映射到 a 而不會繼續映射到 c。一般情況下我們使用 noremap 即可。對於map 以及 noremap 的詳細的介紹可以查看這篇文章:vim 的幾種模式和按鍵映射

另外,在 Vim 中還有 Leader 鍵的概念,它有點像是我們按快捷鍵的時候的 ctrl 或者 cmd。但不同的是我們不需要按住它。例如我們可以爲普通模式下的某個插件的功能或者命令添加如下快捷鍵:

這樣我們就可以先按 Leader 鍵,然後在按 j 來映射到 2j 上。 日常情況下我們會把安裝的插件按照自己喜歡的組合方式配置快捷鍵,這個時候就會經常使用到 Leader 鍵。

Leader 鍵在默認的情況下是 ,,即我們上面的設置的快捷鍵是 ,j 映射到 2j。此外我們可以通過如下配置將 Leader 鍵修改爲其他的按鍵,我的習慣是把 Leader 鍵設置爲空格。

let g:mapleader = "\<Space>"

喜歡使用 Vim 的其中一個原因其實也跟快捷鍵有關,在 Vim 中我們可以相對自由的設置自己喜歡的快捷鍵,而不會像其他編輯器一樣有諸多限制,並且容易跟現有的默認快捷鍵產生衝突。

Vim 中提供了「宏」來方便進行一系列的操作,簡單來說就是在開始前開啓宏的錄製,然後進行一頓操作後停止宏的錄製。這樣會把我們的操作都保存到寄存器中,執行宏的時候會從寄存器上取出來進行回放操作。

一般情況下我會通過 qq 來開啓宏錄製,把相關的操作錄製到名爲 q 的寄存器上(第一個 q 表示開始宏的錄製,第二個 q 表示寄存器)。再按一次 q 將會停止宏的錄製。

例如我們要給 10 行的文字添加相同的前綴和後綴,我們可以開始宏的錄製後進行以下操作:IprefixEscAsuffixEscj對第一行進行操作,然後按 q 停止宏的錄製。最後我們通過 @q 來回放存放在寄存器 q 中的操作來對第二行生效。

宏的錄製和回放

那麼剩下的 8 行難道要按 8 次 @q 來進行回放嗎?顯然,宏跟命令一樣都適用在前面加執行次數的規則,因此我們可以通過 8@q 來完成剩下的工作:

多次回放宏

這種通過錄制和回放宏來執行重複機械式的操作非常有用,我經常會使用宏來處理枚舉,JSON 或者對象的轉換上。

宏作爲 Vim 中的意大利炮非常的強大,這裏只做了最基本的時候介紹,關於宏還有非常多的使用技巧,有機會的話再寫另一篇文章進行介紹。

簡單配置

目前到這裏我們已經學會了如何使用 Vim 進行最基本的光標移動、輸入、刪除和快捷鍵映射等。但是對於一個什麼配置都沒有的 Vim 編輯器來說實在是太簡陋了。因此我們需要對 Vim 進行簡單的配置。

默認情況下,我們可以在 ~/.vimrc 中添加我們自己的個性化配置(如果不存在這個文件需要自行創建)。

顯示相對行號

set number
set relativenumber

通過 set number 可以顯示當前光標所在行的行號,而 set relativenumber 將顯示相對光標所在行的行數,方便我們執行類似 10j4dd 之類對行進行的操作。

當前行號與相對行號

縮進設置

在普通模式下,我們可以通過 >> 或者 << 來調整縮進,有點類似與普通的編輯器中的 tabshift + tab。但每一次都按兩下 > 來進行縮進顯得非常的繁瑣,因此我們可以添加如下快捷鍵映射:

nnoremap < <<
nnoremap > >>

這樣我們就可以在普通模式下愉快的使用 >< 縮進了!

說到縮進那麼就免不了提到用空格還是用 Tab 了。理想的情況下我是不希望我一直按空格的,但是我又不希望使用 Tab 來進行縮進,所以我會添加如下配置:

set shiftwidth=2
set tabstop=2
set autoindent
set smarttab
set expandtab

這裏的意思是我們使用 2 個空格來進行縮進,不管是在普通模式還是在插入模式中按 > 或者 Tab 進行縮進都使用空格。如果想了解 Vim 縮進設置推薦看這篇文章:Vim 縮進有關的設置總結

更好的光標跳轉

如果你和我一樣,覺得使用 ^ 或者 $ 來跳轉到行首和行尾很彆扭的話,可以像我一樣考慮添加相關的快捷鍵映射:

noremap H ^
noremap L $
noremap J G
noremap K gg

這裏額外的添加了兩條映射,目的是讓我們大寫的 H/J/K/L 更加符合直覺:我們可以通過 J 或者 K 來到達文件的開頭和結尾,通過 HL 來到達當前行的行首和行尾。簡單來說,它就是光標移動的放大版。

在 VS Code 中使用

按照一般的套路,這裏我們會接着討論如何給 Vim 添加配置以及插件,使得 Vim 更加好用。但絕大多數的人在讀了這篇文章之後也不會有換編輯器的想法,因此我們這裏會聊聊如何在現有的編輯器中更好的使用 Vim 模式進行編輯,這裏我們主要介紹如何在 VS Code 中使用。

在 VS Code 中使用 Vim 模式進行編輯可以通過兩個插件來實現,分別是:

VSCodeVim/Vim

說到 VS Code 上的 Vim 插件就必須要提 VSCodeVim/Vim 了。該插件在 VS Code 中模擬了 Vim 中的模式切換以及基本的命令。但比較可惜的是我們不能複用前面所提到的 .vimrc 配置文件,我們需要在 settings.json 裏通過 JSON 的方式來進行設置,添加快捷鍵等。

{
  "vim.insertModeKeyBindings": [
    {
      "before": ["j", "j"],
      "after": ["<Esc>"]
    }
  ],
  "vim.normalModeKeyBindingsNonRecursive": [
    {
      "before": ["H"],
      "after": ["^"]
    },
    {
      "before": ["L"],
      "commands": ["$"]
    }
  ]
}

除此之外如果我們要通過快捷鍵來調用 VS Code 中的插件的話也是同樣的需要在 settings.json 進行配置。

{
	"vim.normalModeKeyBindings": [
        {
            "before": [":"],
            "commands": [
                "workbench.action.showCommands",
            ]
        }
    ]
}

作爲 Vim 用戶的我是不喜歡這種方式的,我更加希望通過 .vimrc 來複用原先的配置,無縫的切換到 VS Code 中。而 VSCodeVim/Vim 需要重新寫配置,並且配置的過程過於複雜,所以我很快就放棄了這個插件。

但如果是作爲 VS Code 用戶想在 VS Code 中慢慢開始使用 Vim 的編輯模式的話,不失爲一個好的入門插件。

asvetliakov/vscode-neovim

由於 VSCodeVim/Vim 插件配置比較繁瑣,並且使用起來也稍微有點卡。我放棄 Vim 插件在 VS Code 中使用了一段時間,然後我發現了 vscode-neovim 這個插件。

vscode-neovim 插件集成了 neovim 與 VS Code,完美的滿足了我的需求,它能夠讀取現有的 Vim 配置並且使用起來非常的順滑。

只需要在 Vim 的配置中通過 if !exists('g:vscode') 將不需要的 Vim 插件剔除就可以直接使用了。與 neovim 集成的好處就是我們既可以使用部分 Vim 插件又可以使用 VS Code 插件。當然,這也提高了門檻,該插件依賴 neovim 0.5.0+ 以上的版本。對於剛入門 Vim 的同學來說安裝的步驟過於複雜,這裏就不向 Vim 新手推薦了。

熟悉 neovim 的同學感興趣可以試試。或者查看我在 vscode-neovim 中使用的 Vim 配置

最後

雖然本文已經非常的長了,但是依然對於 Vim 中的快速分屏與切換以及其他非常有用的功能或者技巧沒有進行介紹。這裏強烈的建議感興趣的小夥伴看 簡明 Vim 練級攻略 或者 Vim 實用技巧 這本書來進一步的瞭解。

如果是面向 VS Code 用戶的話,本篇文章到這裏就結束了。原本是打算繼續寫 Vim 中相關的一些好用的插件推薦的,但考慮到閱讀本文的同學可能大部分對 Vim 比較陌生,因此就沒有繼續再寫。如果你感興趣的話歡迎留言,後面可以專門寫一篇。

最後,不管看完文章的你是開始使用 Vim 編輯模式還是重新審視目前的輸入習慣,希望大家都能夠有所收穫。

參考

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://www.ahonn.me/blog/the-vim-guide-for-vs-code-users