关键词:VSCode、snippets、Javascript、Vue、Vue3、编码效率
编写代码的痛点
你是一位程序员,三年代码十万行,号称自身早已精通一套 Ctrl+C/V 组合拳,走遍天下都不怕!但每当深夜来临,你又心生不甘,觉得自己天资聪颖,怎可在这些无甚大用的重复字符上虚度光阴。自己是干大事的!自己的时间是无比珍贵的!
你痛下决心,要掌握降龙十八掌此般武艺,彻底摆脱被 Ctrl+C/V 操弄的命运!
提高撸码效率的方式
经过你的仔细研究,你发现现今世界上已经存在了如下这些高效率方案:
- AI 直接生成 —— 这个有点用。但自己仍需要编写绝大部分代码呢,还是看看其他方案吧。
- 代码工程化 —— 这个很有用。主是把公共的代码都抽象出来,需要用到时通过代码引用的方式引入。另外也包括使用一些预处理方案(例如用来表示 HTML 的
Pug)来减少代码量。 - 模板生成器 —— 这个很有用。例如
plop这种微型生成器框架,通过命令行窗口输入简短命令的方式,直接生成带有模板内容的代码文件。 - 编辑器插件 —— 这个很有用。其实就是使用别人编写好的模板代码插入方案(如
Emmet),能帮助提高编码效率,总归是好的。 - 版本管理器 —— 这个很有用。特别是出于某些不可明说的原因需要使用以前版本的代码时。不过作为一个敲码三年的程序员,早就掌握
Git、Svn啦! - Snippets —— 这个也很有用呢。通过给编辑器添加一些必要的配置,在工作区输入提示词后即能选择配置中的代码模板。
- ……
为何要掌握 Snippets
因为 Snippets 是程序员最快提升编码速度的利器。像代码工程化、模板生成器,需要系统化地学习后,才能发挥足够的生产力。编辑器插件,则存在切换语言或环境后会找不到提低方案的窘境。几乎所有的代码编辑器都支持 Snippets,而 VSCode 在登录后更是能把 Snippets 同步到云端来实现异地共用,所以大可放心使用。
Snippets 的优点
- 编辑器内置支持,可指定性较高,能编写符合自己风格的代码模板;
- 作为一种实质上的文本模板,能被几乎所有的编程语言所用;
- 简洁高效,只需要掌握少量的知识点,就能编写出强大的代码模板;
- 可根据使用者自身的编码习惯定制提示词,让模板的引用适应程序员,而不是让程序员适应模板的引用方法。
Snippets 的不足
- 不能制作出一些特殊的模板,例如像 IDEA 那样的提示词转代码:
variable.log->console.log(variable); - 纯手写 Snippets 配置时,需要考虑各种转义字符。不过已经有跨编辑器解决方案;
- 如果需要编写一些极其通用的模板,则需要精通正则表达式才可以,而且写出的模板看起来很复杂,维护难度较高。
Snippets 的关键知识点
这里以 VSCode 的 Snippets 为例,具体可参考官方文档。
编辑器如何打开 Snippets ?
- 首先编辑器中的配置项
"editor.tabCompletion": "on"是必需的。 - 在编辑器左下角选择
管理(Manage)->用户代码片段(User Snippets)->编程语言名称或全局.json,即可以进入 Snippets 编写界面。 - VSCode 内置了大量的 Snippets 模版,如
for、foreach、function、trycatch等,我们声明模板前缀时尽量避开这些名称。
每个模板的结构是怎样的?
key: 模板对象在当前 JSON 中的唯一标识,即key可以是任意的唯一的字符串。下面则是 key 所指向的对象的各个属性。scope:String,作用范围,多个可用英文版逗号分隔,如 "javascript,html"。省略则表示支持所有语言。prefix:String|[String],提示词,是工作区输入关键词后触发模板的入口。body:String|[String],模板内容,使用字符串数组时,每个字符串元素表示一行。description:String,描述当前模板用途的文案,可省略。
Snippet 具体语法有哪些?
Tabstops: Tab 键跳转的断点,如$1、$2……,另外$0表示最后一个 Tab 落脚点。另外${1}等价于$1。Placeholders: Tab 键断点默认的填充串,如${1:Hello World!},工作区出现模板内容后,鼠标落脚点定格在$1处,而且选中了Hello World!这段文字。程序员可以按下 Tab 跳过(即采用默认文本),或者输入内容来替换掉Hello World!,然后再按 Tab 键跳到下一个落脚点。Choice: Tab 键断点处的选项,如${1|Hello,World,!|},工作区出现模板内容后,鼠标落脚点定格在$1处,并且弹出选择框,让程序员选择Hello、World、!中的其中一项来填充到$1处。Variables: Tab 键断点处使用变量的值作为默认填充,如$name或${name:default},前者如果未赋值,则在断点处插入空字符串,后者如果未赋值,则在断点处插入default字符串。如果$name是未定义的,那么name将会作为默认文本填充到断点处。内置的已定义变量包括:TM_SELECTED_TEXT: 键入关键词前选中的文本(如果没有选中文件直接键入则为空字符串)TM_CURRENT_LINE: 当前行的内容,不包括正在键入的提示词部分。触发后不会替换掉已存在的内容,而是在当前行内容和键入的提示词之间插入模板内容TM_CURRENT_WORD: 光标所位于的单词的内容,或者空字符串TM_LINE_INDEX: 从 0 开始计数,当前文档总行数TM_LINE_NUMBER: 从 1 开始计数,当前文档总行数TM_FILENAME: 当前文档的文件名,包括后缀部分TM_FILENAME_BASE: 当前文档的文件名,不包括后缀部分TM_DIRECTORY: 当前文档的所在目录,是个绝对地址TM_FILEPATH: 当前文档的文件路径,是个绝对地址RELATIVE_FILEPATH: 当前文档的相对于 VSCode 所打开的工作区或文件夹的路径,是个相对地址CLIPBOARD: 你的粘贴板中当前的内容,就是你刚刚复制的文本WORKSPACE_NAME: VSCode 所打开的工作区或文件夹的名称,仅仅是名称WORKSPACE_FOLDER: VSCode 所打开的工作区或文件夹的绝对路径,包括自身的名称CURSOR_INDEX: 从 0 开始计数,当前属于第几个在闪的光标CURSOR_NUMBER: 从 1 开始计数,当前属于第几个在闪的光标CURRENT_YEAR: 当前年份CURRENT_YEAR_SHORT: 当前年份的最后两位数字CURRENT_MONTH: 当前月份,不足两位在在前方补 0CURRENT_MONTH_NAME: 当前月的完整英文名称CURRENT_MONTH_NAME_SHORT: 当前月的英文名称简写(通常是三个字母)CURRENT_DATE: 今天是当前月的第几天,不足两位在在前方补 0CURRENT_DAY_NAME: 今天是星期几,显示英文名称CURRENT_DAY_NAME_SHORT: 今天是星期几,显示英文名称简写(通常是三个字母)CURRENT_HOUR: 现在是二十四小时制的第几个小时,不足两位在在前方补 0CURRENT_MINUTE: 现在是当前小时的第几分钟,不足两位在在前方补 0CURRENT_SECOND: 现在是当前分钟的第几秒,不足两位在在前方补 0CURRENT_SECONDS_UNIX: 现在距离 1997年01月01日 00:00:00 有多少毫秒CURRENT_TIMEZONE_OFFSET: 当前地区与 UTC 标准 0 时区相差多少时间,格式为[+-]HH:MMRANDOM: 一个六位的十进制随机数RANDOM_HEX: 一个六位的十六进制随机数UUID: 一个第 4 版的 UUID 串BLOCK_COMMENT_START: 块级注释起始部分,例如在 JavaScript 文件中是/*,在 HTML 中则是<!--BLOCK_COMMENT_END: 块级注释结尾部分,例如在 JavaScript 文件中是*/,在 HTML 中则是-->LINE_COMMENT: 行级注释,例如在 JavaScript 文件中是//
Snippets 最强大的武器是什么?
当然是 EBNF 中的转换结构!,标准格式如下:
${variable/regex/format/options}
- variable 就是上面的
Variables中的其中一个; - regex 则是一个标准的 JavaScript 正则字面量;
- format 则可以是普通的文本,也可以是一个正则结果变量与文本的结合:
$0表示整个正则匹配到的完整内容$1表示第一个捕获组匹配到的内容$2表示第二个捕获组匹配到的内容- ……
$9也可能不存在第九个捕获组,那么它就是空字符串。由此我们可知,在 format 中,$0~$N不会表示 Tab 断点,而是表示正则匹配的结果集。这是重点,务必牢记。另外,这里捕获结果的内容还可以进一步处理,就拿当前$9为例:${9:/downcase}表示把第九个捕获组匹配到的内容全部转换为小写${9:/upcase}表示把第九个捕获组匹配到的内容全部转换为大写${9:/capitalize}表示把第九个捕获组匹配到的内容转换为首字母大写的形式${9:/camelcase}表示把第九个捕获组匹配到的内容转换为驼峰形式${9:/pascalcase}表示把第九个捕获组匹配到的内容转换为帕斯卡形式
- options 就是 JavaScript 正则选项,如
i、g等
有了以上的基础,我们就可以通过案例来进一步理解如何编写极其实用的代码模板了!
Snippets 的案例一
{
// 这是一个立即执行函数模板
// 当你选中一行或多行代码后
// 再输入 iife 并选择本模板
// 选中的代码内容会自动成为立即执行函数的函数体并默认选中
// 你可以快速保留或替换这些选中的内容
"iife": {
"prefix": "iife",
"body": [
"(function () {",
"\t${1:$TM_SELECTED_TEXT}",
"}());",
"$0"
],
"description": "立即执行函数包装选中的代码"
},
}
Snippets 的案例二
{
// 这是一个打印变量的模板
// 你需要先复制一个变量名如 age
// 然后在新的一行输入 logv 并选择本模板
// 然后该行就会生成一行代码,如 `console.log('age: ', age);`
"logv": {
"prefix": "logv",
"body": [
"console.log('${CLIPBOARD}: ', ${CLIPBOARD});$0"
],
"description": "打印粘贴板上的变量"
},
}
Snippets 的案例三
本人编写 Vue3 页面的风格如下:
- 每个页面文件都用一个文件夹包裹着
- 页面文件直接使用 index.vue 命名
- 每个页面都必须标记上作者、生成日期、页面描述
- 页面在 Vue3 的识别名采用父级文件夹的名称
- 页面根节点的类名采用父级文件夹的名称的全小写
最终生成的模板格式如下:
{
"vue3page": {
"prefix": "vue3page",
"body": [
"<script setup>",
"/**",
" * @author ZhongyiChen",
" * @date ${CURRENT_YEAR}-${CURRENT_MONTH}-${CURRENT_DATE}",
" * @description $1",
" */",
"defineOptions({",
"\tname: '${2:${TM_DIRECTORY/(.*\\\\)//}}'",
"})",
"</script>",
"",
"<template>",
"\t<${4:section} class=\"${3:${TM_DIRECTORY/.*\\\\([^\\\\]*)$/${1:/downcase}/}}\">",
"\t\t$5",
"\t</${4:section}>",
"</template>",
"",
"<style lang=\"scss\">",
".${3:${TM_DIRECTORY/.*\\\\([^\\\\]*)$/${1:/downcase}/}}{$6",
"}",
"</style>",
"$0"
],
"description": "新增一个 Vue3 的模板页面"
},
}
可以看到:由于没有直接引用父级目录名称的变量,所以这里采用了 ${TM_DIRECTORY/(.*\\\\)//} 来把父级目录的路径清除(format 为空表示替换的内容为空)。
另外,由于构建正则符号 \ 时需要转义,所以正则字面量中需要出现 \\,而在 body 字符串中,生成正则字面量前,也需要为 \ 转义,所以匹配一个 \,就需要共四个即 \\\\ 来构建 format。
其他部分如果不理解,那么你应当回到文章开头继续学习一遍。
写在最后
写这篇文章,花了我两个晚上。希望对你有用。