更多文章,參見大搜車技術部落格: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
,選中配置使用者程式碼片段
,這時候會彈出
可以在現有的程式碼片段檔案上新增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(非必填).
上述英文解釋引自官方,怕翻譯不到位影響讀者理解,就不作翻譯了。
下面用一張圖展示在使用中各個屬性對應的位置
上圖左側框中的為prefix
,右側圈中的為description
,description
下方的為body
部分內容
Snippet語法
Snippet
的語法集中在body
上。
Tabstops
: 即$1
、$2
等。使用$1
、$2
等表示按下鍵盤tab
後游標將要指向的位置。根據數字大小表示先後順序。$0
表示游標最後指向的位置。可以存在多個相同的Tabstops
,並會同步更新。
以下是多個相同的Tabstops同步更新的示例
{
"For_Loop": {
"prefix": "for",
"scope": "javascript,typescript",
"body": [
"for (const ${2:element} of ${1:array}) {",
"\tconst item = $1[$2];$0",
"}"
],
"description": "For Loop"
}
}
複製程式碼
以下是使用上述snippet的過程
Placeholders
:佔位符。使用者直接跳過Tabstops
不輸入新值時,會使用佔位符。它還能內嵌。
以下是佔位符內嵌的示例
{
"placeholder": {
"prefix": "ph",
"body": "${1:hello ${2:world}}$0"
}
}
複製程式碼
以下是使用上述snippet的過程
Choice
: 可選的佔位符
以如下snippet為例
{
"choice": {
"prefix": "ch",
"body": "${1|hello,hi,how are you|}"
}
}
複製程式碼
輸入ch
按下tab鍵後,就會顯示
Choice
中,,
、|
、$
、}
、\
可以使用\
轉義;其他情況有且僅$
、}
、\
可以使用\
轉義,其他字元無法轉義, 詳見https://code.visualstudio.com/docs/editor/userdefinedsnippets#_grammar
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(實際操作不規範,僅僅為了精簡)
注:使用transform末尾必須加/
,才能被識別。如${TM_FILENAME_BASE/(global).*/$1/
}
其中1
、2
意義相同
程式碼片段
"transform": {
"prefix": "tr",
"body": "${TM_FILENAME_BASE/(global).*/$1/}" // 輸出: global
}
// 待匹配字串 global-js
// 正規表示式 (global).*
// 全部匹配項 global-js
// 分組捕獲組 ['global'],即$1=global
// $1替換global-js,即輸出 global
複製程式碼
3
表示對匹配到的相應分組捕獲組轉化成大寫,或小寫,或首字母大寫,以替換正規表示式匹配到的全部字串
程式碼片段(該片段僅展示轉化成大寫)
"transform": {
"prefix": "tr",
"body": "${TM_FILENAME_BASE/(global).*/${1:/upcase}/}" // 輸出: GLOBAL
}
// 待匹配字串 global-js
// 正規表示式 (global).*
// 全部匹配項 global-js
// 分組捕獲組 ['global'],即$1=global
// 經${1:/upcase}轉換後,為GLOABl
// ${1:/upcase}替換global-js,即輸出GLOABl
複製程式碼
4
表示匹配成功時,將if
所述語句完全替換正規表示式匹配項。
程式碼片段
"transform": {
"prefix": "tr",
"body": "${TM_FILENAME_BASE/(global)/${1:+if}/}", //輸出: if-js
// 待匹配字串 global-js
// 正規表示式 (global)
// 全部匹配項 global
// 分組捕獲組 ['global'],即$1=global
// $1匹配成功, if替換global,即輸出if-js
"body": "${TM_FILENAME_BASE/(global).*/${1:+if}/}" //輸出: if
// 待匹配字串 global-js
// 正規表示式 (global).*
// 全部匹配項 global-js
// 分組捕獲組 ['global'],即$1=global
// $1匹配成功, if替換global-js,即輸出if
}
複製程式碼
上述3、4兩個示例匹配失敗時,不做轉換,即仍輸出global.js
5
表示匹配成功,且相應的分組捕獲成功時,將if
所述語句完全替換正規表示式匹配項;
匹配成功,且相應的分組捕獲為空時,將else
所述語句完全替換正規表示式匹配項;
匹配失敗時,else
所述語句即為處理後的變數
程式碼片段
"transform": {
"prefix": "tr",
"body": "${TM_FILENAME_BASE/(global)/${1:?if:else}/}", //輸出: if-js
// 待匹配字串 global-js
// 正規表示式 (global)
// 全部匹配項 global
// 分組捕獲組 ['global'],即$1=global
// $1匹配成功, if替換global,即輸出if-js
"body": "${TM_FILENAME_BASE/global/${1:?if:else}/}", //輸出: else-js
// 待匹配字串 global-js
// 正規表示式 global
// 全部匹配項 global
// 分組捕獲組 [],$1對應的分組捕獲為空
// else替換global,即輸出else-js
"body": "${TM_FILENAME_BASE/(globald)/${1:?if:else}/}", //輸出: else
// 待匹配字串 global-js
// 正規表示式 (globald),匹配失敗
// 全部匹配項 null
// 分組捕獲組 無
// 輸出 else
}
複製程式碼
6
同7
,表示匹配成功,且分組捕獲(正規表示式中()
匹配到的項)成功時,不變;
匹配成功,且分組捕獲為空時,將else
所述語句完全替換正規表示式匹配項;
匹配失敗時,else
所述語句即為處理後的變數
程式碼片段
"transform": {
"prefix": "tr",
"body": "${TM_FILENAME_BASE/(global)/${1:else}/}", //輸出: global-js
// 待匹配字串 global-js
// 正規表示式 (global)
// 全部匹配項 global
// 分組捕獲組 ['global'],即$1=global
// 不變,輸出global-js
"body": "${TM_FILENAME_BASE/global/${1:else}/}", //輸出: else-js
// 待匹配字串 global-js
// 正規表示式 global
// 全部匹配項 global
// 分組捕獲組 [],$1對應的分組捕獲為空
// else替換global,輸出else-js
"body": "${TM_FILENAME_BASE/(globald)/${1:else}/}", //輸出: else
// 待匹配字串 global-js
// 正規表示式 (globald),匹配失敗
// 全部匹配項 null
// 分組捕獲組 無
// 輸出 else
}
複製程式碼
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
然後可以新增如下
{
"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)"
]
}
複製程式碼
只需要輸入元件名,就可以一次性生成必備的格式。
最後
安利一下prettier,配合snippet使用,簡直絕配。