如何写一个VsCode插件(react-antd-helper的开发纪实)

背景

每次在开发一个新页面的时候,都要从头开始写一些固定的代码,或者每次在写一些特定场景需求的时候,都要从头开始写需要固定的逻辑功能,而这些工作,都是重复的,而不同的开发者,在实现同一功能时,又表现出五花八门的写法,于是,我决定,写一个工具,用来快速生成常用模板,这不仅对于提升效率有所帮助,对于统一规范也有一定的帮助。
当然这个工具可以是cli或者vscode snippets,但个人开发的东西,因为没有太大知名度,使用者主动去用的概率会小一点,于是,我打算开发一个Vscode插件,接下来就是开发过程。

开发过程

一、安装脚手架

环境准备

  • NodeJS
  • Git

安装

npm install -g yo generator-code

生成项目框架

yo code

根据脚手架提示选择自己的配置,大概如下图所示

# ? What type of extension do you want to create? New Extension (TypeScript) // 使用语言
# ? What's the name of your extension? react-antd-helper // 插件名称
### Press <Enter> to choose default for all options below ###
# ? What's the identifier of your extension? 
# ? What's the description of your extension? 一款快速生成react+antd各种场景的模板插件 // 插件描述
# ? Enable stricter TypeScript checking in 'tsconfig.json'? Yes
# ? Setup linting using 'tslint'? Yes
# ? Initialize a git repository? Yes
# ? Which package manager to use? npm

然后会生成一个“react-antd-helper”的项目,其中主要页面结构如下

|--out // 编译后的输出目录
  |-- extension.js
|--src // 核心逻辑
  |-- extension.ts // 插件内容写在这里
|--package.json // 发布相关的信息都在这里配置
|--CHANGELOG.md // 发布日志

其中,插件的核心功能都会在extension.ts中,当你初始化完一个项目后,改文件的内容大概如下

// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
import * as vscode from 'vscode';

// this method is called when your extension is activated
// your extension is activated the very first time the command is executed
export function activate(context: vscode.ExtensionContext) {
    
    // Use the console to output diagnostic information (console.log) and errors (console.error)
    // This line of code will only be executed once when your extension is activated
    console.log('Congratulations, your extension "react-antd-helper" is now active!');

    // The command has been defined in the package.json file
    // Now provide the implementation of the command with registerCommand
    // The commandId parameter must match the command field in package.json
    let disposable = vscode.commands.registerCommand('react-antd-helper.helloWorld', () => {
        // The code you place here will be executed every time your command is executed
        // Display a message box to the user
        vscode.window.showInformationMessage('Hello World from react-antd-helper!');
    });
    context.subscriptions.push(disposable);
}

// this method is called when your extension is deactivated
export function deactivate() {}

这里最主要的内容就是

 let disposable = vscode.commands.registerCommand('react-antd-helper.helloWorld', () => {
        // The code you place here will be executed every time your command is executed
        // Display a message box to the user
        vscode.window.showInformationMessage('Hello World from react-antd-helper!');
    });

注册了一个指令react-antd-helper.helloWorld,然后做了一个回调。我们可以在里面写我们自己的逻辑,这里我先不冗余写了,不同的插件实现方式也不一样,我会先文章结尾附上我个人插件的源码。

调试

我们直接点击VsCode调试按钮Run Extension进行调试即可,这时会打开一个新的窗口Extension Development Host(你打开的文件夹),然后就可以在这个页面进行调试了


同时,也可以在新打开的页面,输入command + shift + P看到我们注册的指令,并执行测试

发布配置

对于VsCode插件来说,插件内容比较灵活,开发内容没有太固定的形式,这里我们就不细说了,而其发布配置,有好多重要的点也是比较通用的点,比较重要,下面我们详细介绍一下发布配置package.json

{
    "name": "react-antd-helper",
    "displayName": "react-antd-helper",  // // 展示名称
    "description": "一款快速生成react+antd各种场景的模板插件", // 插件描述
    "version": "0.0.7", // 插件版本,可以自己修改,也可以在发布时自动改,生成的插件包会带这个版本
    "repository": "https://github.com/sorryljt/react-antd-helper",// 仓库地址
    "publisher": "sorryljt", // 发布者
    "license": "MIT",
    "icon": "logo.png", // 在插件市场展示的插件logo,这里得是一个相对路径的地址
    "engines": {
        "vscode": "^1.65.x" // 适配的vscode版本,版本过高,将导致低版本vscode安装失败
    },
    "categories": [
        "Other"
    ],
    "activationEvents": [ // 所有的插件中的指令要在这里注册
        "onCommand:react-antd-helper.helloWorld",
        "onCommand:react-antd-helper.generateSimplePage",
        "onCommand:react-antd-helper.generateSearchFormAndTable",
        "onCommand:react-antd-helper.generateModal"
    ],
    "main": "./out/extension.js", // 入口文件
    "contributes": {
        "commands": [ // 指令的详情,可以在 `command` + `shift` +   `P`时看到
            {
                "command": "react-antd-helper.generateSimplePage",
                "title": "生成一个简单页面",
                "category": "快速生成React&Antd页面"
            },
            {
                "command": "react-antd-helper.generateSearchFormAndTable",
                "title": "生成一个表单检索&表格页面",
                "category": "快速生成React&Antd页面"
            },
            {
                "command": "react-antd-helper.generateModal",
                "title": "生成一个弹窗页面",
                "category": "快速生成React&Antd页面"
            }
        ],
        "menus": { // 菜单配置
            "explorer/context": [ // 文件夹菜单
                {
                    "group": "2_workspace@1",// 决定排序方式,可参考https://www.bookstack.cn/read/VS-Code-Extension-Doc-ZH/docs-extensibility-reference-contribution-points.md#4hamih
                    "when": "explorerResourceIsFolder", // 执行时机 可参考https://liiked.github.io/VS-Code-Extension-Doc-ZH/#/references/when-clause-contexts
                    "submenu": "react-antd-helper/submenu/generate" // 可以设置该菜单为子菜单
                 }
            ],
            "react-antd-helper/submenu/generate": [ // 对应id的子菜单配置,在这里可以配置其对应的指令和排序
                {
                    "command": "react-antd-helper.generateSimplePage",
                    "group": "1_generate@1"
                },
                {
                    "command": "react-antd-helper.generateSearchFormAndTable",
                    "group": "1_generate@2"
                },
                {
                    "command": "react-antd-helper.generateModal",
                    "group": "1_generate@3"
                }
            ]
        },
        "submenus": [ // 子菜单的配置,以及其标题显示
            {
                "id": "react-antd-helper/submenu/generate",
                "label": "快速生成一个react&antd页面"
            }
        ]
    },
    "scripts": {
        "vscode:prepublish": "yarn run compile",
        "compile": "tsc -p ./",
        "watch": "tsc -watch -p ./",
        "pretest": "yarn run compile && yarn run lint",
        "lint": "eslint src --ext ts",
        "test": "node ./out/test/runTest.js"
    },
    "devDependencies": {
        "@types/vscode": "^1.65.0",
        "@types/glob": "^7.2.0",
        "@types/mocha": "^9.1.1",
        "@types/node": "16.x",
        "@typescript-eslint/eslint-plugin": "^5.30.0",
        "@typescript-eslint/parser": "^5.30.0",
        "eslint": "^8.18.0",
        "glob": "^8.0.3",
        "mocha": "^10.0.0",
        "typescript": "^4.7.4",
        "@vscode/test-electron": "^2.1.5"
    }
}

在上述的package.json文件介绍中,大部分我通过标记注释的方式来说明用法,这里再特殊说明一下我在开发时候,踩过坑的几个配置项

  • contributes.commands中注册的指令,要在src/extension.ts中实现,反之,在src/extension.ts中实现的指令,也要在其中注册
  • 定义指令后,还需要在activationEvents中激活"onCommand:react-antd-helper.helloWorld"
  • 菜单的配置,我们大部分配置可以官网发布配置中找到,但是其中并没有提起对于资源管理器上下文菜单的子菜单配置,我是通过看其他插件的开发代码找到了配置方法,就像我设置的submenus那种
  • 关于logo的配置,找了很久,因为默认是不带logo的,并且发布配置中也没有介绍,后来找到了这里https://www.bookstack.cn/read/VS-Code-Extension-Doc-ZH/docs-references-extension-manifest.md

发布插件

插件发布官网提供了比较全的教程,这里做个总结,具体发布看这两篇官网链接就够了

安装vsce
npm install -g vsce
打包成 vsix 文件
vsce package
发布

发布之前,首先你得有一个Personal Access Tokens
按照上面的文档,创建完,可以采用

vsce publish

也可以采用可视化上传vsix文件的方式来发布


发布成功后就可以在扩展市场看到自己的插件了。

遇到的问题

  • windows系统文件反斜杠的问题
    因为我的插件是需要写入文件的,我当时用的是的mac开发,当有些同学试用的时候,发现写入文件失败,后来才发现,我忽略了windows系统下文件路径“\”反斜杠的问题。

  • 不能使用node 原生path来获取命令操作路径,在vscode开发环境中,我们不能使用path中的__dirname以及path.resolve(./)来获取操作路径,应该使用registerCommand方法的回调参数,如下

    let generateModal = vscode.commands.registerCommand('react-antd-helper.generateModal', (e) => {
        console.log(e) // 其中e为文件信息,但e.fsPath才代表不同操作系统下的真实路径
    });

其中e为文件信息,但e.fsPath才代表不同操作系统下的真实路径,所以在拼接文件名时要使用e.faPath,并且拼接符号的正反斜杠,要使用node path下的sep变量。这样就可以自动区分出不同系统下的文件路径了,最终可以是这样拼写${fsPath}${nodepath.sep}index.tsx

总结

本文介绍了我个人开发的一个vscode插件的历程,另外也欢迎大家使用我的vscode插件,功能如下
安装了插件后(react-antd-helper),在文件夹出鼠标右击,即可出现如下的菜单


点击生成需要的页面,即可生成例如下面的较完整,可运行的react代码

环境大家使用哦!
也环境大家共建,本插件的源码如下
https://github.com/sorryljt/react-antd-helper

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,743评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,296评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,285评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,485评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,581评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,821评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,960评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,719评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,186评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,516评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,650评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,329评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,936评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,757评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,991评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,370评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,527评论 2 349

推荐阅读更多精彩内容