更多文章,參見大搜車技術部落格:blog.souche.com/
大搜車無線開發中心持續招聘中,前端,Nodejs,android 均有 HC,簡歷直接發到:sunxinyu@souche.com
前言
大家都是出來寫程式碼的,少不了要寫上千萬來行程式碼,其中重複性的程式碼佔比又會很大。那麼如何避免一次又一次寫重複性的程式碼這無聊的事情呢?除了程式碼自身的優雅、可複用,Jetbrains系如Intellij IDEA的Live Templates
或Visual Studio Code的Snippet
等內建工具都能很大程度上幫我們少寫很多重複性的程式碼。下面就vsc的Snippet
,結合demo,講講它的用法。
建立Snippet
快捷鍵Cmd + Shift + P
或F1
開啟命令視窗,輸入snippet
,選中配置使用者程式碼片段
,這時候會彈出
![【VSC】Snippets不完全指南](https://i.iter01.com/images/d06ee79f6aab25957676302f037a765fb053cf54e06749d19b72fc7a24b12bd5.png)
可以在現有的程式碼片段檔案上新增snippet,也可以自己新建程式碼片段檔案。
Snippets片段以JSON格式定義,官方提供的例子如下
{
"For_Loop": {
"prefix": "for",
/**
* 在全域性程式碼片段檔案新增snippet時,
* 通過該項來指定該snippet使用範圍,
* 如下所示,在檔案以`.js`或者`.ts`結尾時可使用該snippet
*/
"scope": "javascript,typescript",
"body": [
"for (const ${2:element} of ${1:array}) {",
"\t$0",
"}"
],
"description": "For Loop"
}
}
複製程式碼
For_Loop
is the snippet name(後續在指定快捷鍵繫結的時候會用到).prefix
defines how this snippet is selected from IntelliSense and tab completion. In this casefor
.(字首支援N:1,比如prefix
值為["for","fof"]
,則for
和fof
對應同一條程式碼片段)body
is the content and either a single string or an array of strings of which each element will be inserted as separate line.description
is the description used in the IntelliSense drop down(非必填).
上述英文解釋引自官方,怕翻譯不到位影響讀者理解,就不作翻譯了。
下面用一張圖展示在使用中各個屬性對應的位置
![【VSC】Snippets不完全指南](https://i.iter01.com/images/edfee35df4edcbac21f48974a581cd119a2863eec5e011ed8bd88d5ce75ad07f.png)
prefix
,右側圈中的為description
,description
下方的為body
部分內容
Snippet語法
Snippet
的語法集中在body
上。
Tabstops
: 即$1
、$2
。使用$1
、$2
等表示按下鍵盤tab
後游標將要指向的位置。根據數字大小表示先後順序。$0
表示游標最後指向的位置。可以存在多個相同的Tabstops
,並會同步更新。
以如下snippet為例
{
"For_Loop": {
"prefix": "for",
"scope": "javascript,typescript",
"body": [
"for (const ${2:element} of ${1:array}) {",
"\tconst item = $1[$2];$0",
"}"
],
"description": "For Loop"
}
}
複製程式碼
以下是使用上述snippet的過程
![【VSC】Snippets不完全指南](https://i.iter01.com/images/2a70500e8384770e8b31cee5ae3f3d567ecf3d7862f24e5ead2b07ea69cc0c2e.gif)
Placeholders
:佔位符。使用者直接跳過Tabstops
不輸入新值時,會使用佔位符。它還能內嵌。
以如下snippet為例
{
"placeholder": {
"prefix": "ph",
"body": "${1:hello ${2:world}}$0"
}
}
複製程式碼
以下是使用上述snippet的過程
![【VSC】Snippets不完全指南](https://i.iter01.com/images/af7a7ab7e893f13ce3f359c22c6f0a6dc77c74cadd35b0ecc11e724460f8d85f.gif)
Choice
: 可選的佔位符
以如下snippet為例
{
"choice": {
"prefix": "ch",
"body": "${1|hello,hi,how are you|}"
}
}
複製程式碼
輸入ch
按下tab鍵後,就會顯示
![【VSC】Snippets不完全指南](https://i.iter01.com/images/944a6827635c027e23c097d9a1b1cf383a86f979adb99ee1bd6daa0af63159ed.png)
Variables
: 官方定義的變數
TM_SELECTED_TEXT:當前選定的文字或空字串;
TM_CURRENT_LINE:當前行的內容;
TM_CURRENT_WORD:游標所處單詞或空字串
TM_LINE_INDEX:行號(從零開始);
TM_LINE_NUMBER:行號(從一開始);
TM_FILENAME:當前文件的檔名;
TM_FILENAME_BASE:當前文件的檔名(不含字尾名);
TM_DIRECTORY:當前文件所在目錄;
TM_FILEPATH:當前文件的完整檔案路徑;
CLIPBOARD:當前剪貼簿中內容。
時間相關
CURRENT_YEAR: 當前年份;
CURRENT_YEAR_SHORT: 當前年份的後兩位;
CURRENT_MONTH: 格式化為兩位數字的當前月份,如 02;
CURRENT_MONTH_NAME: 當前月份的全稱,如 July;
CURRENT_MONTH_NAME_SHORT: 當前月份的簡稱,如 Jul;
CURRENT_DATE: 當天月份第幾天;
CURRENT_DAY_NAME: 當天周幾,如 Monday;
CURRENT_DAY_NAME_SHORT: 當天周幾的簡稱,如 Mon;
CURRENT_HOUR: 當前小時(24 小時制);
CURRENT_MINUTE: 當前分鐘;
CURRENT_SECOND: 當前秒數。
註釋相關
BLOCK_COMMENT_START: 在PHP中輸出 /* ,在HTML中輸出 <!--
BLOCK_COMMENT_END: 在PHP中輸出 */ ,在HTML中輸出 -->
LINE_COMMENT: 在PHP中輸出 // ,在HTML中輸出 <!-- -->
複製程式碼
Variable transforms
變數轉換可將變數的值格式化處理後插入預定的位置。 它包括三個部分:
- 正規表示式
- 格式字串
- 正規表示式匹配選項
正規表示式和正規表示式匹配選項不作解釋。
格式字串在官方文件的Grammar
中如下
format ::= '$' int | '${' int '}'
| '${' int ':' '/upcase' | '/downcase' | '/capitalize' '}'
| '${' int ':+' if '}'
| '${' int ':?' if ':' else '}'
| '${' int ':-' else '}' | '${' int ':' else '}'
複製程式碼
假設某整體片段為${variable/regexp/(format|text)/options}
,再結合上述語法,
整體片段可變為
${var_name/regular_expression/$1/options}
${var_name/regular_expression/${1}/options}
${var_name/regular_expression/${1:/upcase}/options}
///downcase
和/capitalize
使用方式同/upcase
${var_name/regular_expression/${1:+if}/options}
${var_name/regular_expression/${1:?if:else}/options}
${var_name/regular_expression/${1:-else}/options}
${var_name/regular_expression/${1:else}/options}
${var_name/regular_expression/text/options}
上述format
中$
後的數字1表示分組捕獲組(正規表示式中()
匹配到的陣列)的第1項,同理$2表示分組捕獲想第2項。
現有檔名global-js.json
,以demo說明上述含義。為了精簡,輸出值直接以body末尾註釋表示,另外多個body直接放在一個snippet(實際操作不規範,僅僅為了精簡)
其中1
、2
相同
程式碼片段
"transform": {
"prefix": "tr",
"body": "${TM_FILENAME_BASE/(global).*/$1/}" // 輸出: global
}
複製程式碼
3
表示對匹配到項轉化成大寫,或小寫,或首字母大寫
程式碼片段
"transform": {
"prefix": "tr",
"body": "${TM_FILENAME_BASE/(global).*/${1:/upcase}/}" // 輸出: GLOBAL
}
複製程式碼
4
表示匹配成功時,將if
所述語句完全替換正規表示式匹配項。
程式碼片段
"transform": {
"prefix": "tr",
"body": "${TM_FILENAME_BASE/(global)/${1:+if}/}", //輸出: if-js
"body": "${TM_FILENAME_BASE/(global).*/${1:+if}/}" //輸出: if
}
複製程式碼
5
表示匹配成功,且分組捕獲成功時,將if
所述語句完全替換正規表示式匹配項;
匹配成功,且分組捕獲失敗時,將else
所述語句完全替換正規表示式匹配項;
匹配失敗時,else
所述語句即為處理後的變數
程式碼片段
"transform": {
"prefix": "tr",
"body": "${TM_FILENAME_BASE/(global)/${1:?if:else}/}", //輸出: if-js
"body": "${TM_FILENAME_BASE/global/${1:?if:else}/}", //輸出: else-js
"body": "${TM_FILENAME_BASE/(globald)/${1:?if:else}/}", //輸出: else
}
複製程式碼
6
同7
,表示匹配成功,且分組捕獲(正規表示式中()
匹配到的項)成功時,不變;
匹配成功,且分組捕獲失敗時,將else
所述語句完全替換正規表示式匹配項;
匹配失敗時,else
所述語句即為處理後的變數
程式碼片段
"transform": {
"prefix": "tr",
"body": "${TM_FILENAME_BASE/(global)/${1:else}/}", //輸出: global-js
"body": "${TM_FILENAME_BASE/global/${1:else}/}", //輸出: else-js
}
複製程式碼
8
表示匹配成功時,將text
完全替換正規表示式匹配項;
匹配失敗時,不變
程式碼片段
"transform": {
"prefix": "tr",
"body": "${TM_FILENAME_BASE/(global)/text/}", //輸出: text-js
}
複製程式碼
Placeholder Transform
本質與Variable Transform
的第8條一致,只是正規表示式變為佔位符常量
程式碼片段
"transform": {
"prefix": "tr",
"body": "$1 -> ${1/placeholder/text/}", //輸入: placeholder 輸出: placeholder -> text
}
複製程式碼
快捷鍵匯入snippet
首先開啟keybindings.json
![【VSC】Snippets不完全指南](https://i.iter01.com/images/23ea29e34483904c24c3e00a7925c86791c1b4b64df5cee688d907fb70590f3a.png)
然後可以新增如下
{
"key": "cmd+k 1",
"command": "editor.action.insertSnippet",
"when": "editorTextFocus",
"args": {
"snippet": "console.log($1)$0"
}
}
複製程式碼
亦或者匯入已有的snippet
{
"key": "cmd+k 1",
"command": "editor.action.insertSnippet",
"when": "editorTextFocus",
"args": {
"langId": "csharp", // 語言包,如javascript,typescript
"name": "myFavSnippet" // 對應最開始提過的name,如下面使用場景的`import snippet`
}
}
複製程式碼
使用場景
- 用
snippet
寫snippet
"import snipppet": {
"prefix": "sni",
"body": ["\"$1\": {", "\t\"prefix\": \"$2\",", "\t\"body\": [\"$3\"]", "}"]
},
複製程式碼
加個可選的description
"snipppet": {
"prefix": "sni",
"body": [
"\"$1\": {",
"\t\"prefix\": \"$2\",",
"\t\"body\": [\"$3\"]${4:,\n\t\"description\":\"${5}\"}",
"}"
]
}
複製程式碼
- 某些固定格式的陣列物件
如某個陣列長度較大的物件{title:'input',dataIndex:'input'}
,該陣列中物件的每個title
或dataIndex
不盡相同,則可以寫個臨時snippet,直接生成該結構。
"templates": {
"prefix": "tem",
"body": [",{dataIndex:$1,", "title:$2}"],
"description": ""
}
複製程式碼
- 某些固定內容的檔案或者某些常用的
import
組合
"muji store file": {
"prefix": "store",
"body": [
"import { createStore } from '@souche-f2e/muji'",
"",
"type IState = {",
"",
"}",
"const state: IState = {",
"",
"}",
"const store = createStore({",
" state,",
" reducers: {",
" ",
" },",
" effects: {",
" ",
" },",
"})",
"export default store"
]
},
複製程式碼
- muji react 下的pages檔案
"index.tsx under pages": {
"prefix": "ind",
"scope": "typescriptreact",
"body": [
"import React, { Component } from 'react'",
"import { dispatch, IRootState } from '@@/store'",
"import { connect } from '@souche-f2e/muji'",
"const mapStateToProps = (state: IRootState) => state.${1/(.*)/${1:/downcase}/}",
"",
"interface I$1Props extends ReturnType<typeof mapStateToProps> {",
" ",
"}",
"interface I${1}State {",
"",
"}",
"class $1 extends Component<I${1}Props, I${1}State> {",
" render() {",
" return ${3:null}",
" }",
"}",
"export default connect(mapStateToProps)($1)"
]
}
複製程式碼
只需要輸入元件名,就可以一次性生成必備的格式。
![【VSC】Snippets不完全指南](https://i.iter01.com/images/943826965b31a0da32e4ba1f9f9823ca038c1d8a4018b02c8b3597636db63372.gif)