61

记一次前端"vscode插件编写实战"超详细的分享会(上)

记录了我在组内的技术分享, 有同样需求的同学可以参考一下
分享全程下来时间大约 77分钟

image

一. vscode我们的战友

vscode本身都是用浏览器实现的, 使用的技术为Electron, 可以说vscode本身对我们前端工程师很友好, 在vscode上有很多优秀的插件供开发者使用, 那你有没有想过开发一款适合自己团队使用的插件?
写文章之前我在网上找了很多资料, 但视频方面的资料太少了, 官方网站也并没有被很好很完整的汉化, 文章还是有一些的但大多举的例子不好运行, 需要读者自己配置一堆东西, 官网到是提供了再github上的一套例子, 但都是用ts编写并且部分运行都有问题, 里面的思维逻辑与书写方式也不适合入门学习, 所以我才在此写一篇更加易懂的分享教程, 希望可以让刚入门的同学更好的学习起来, 话不多说我们一步步深入探索.

二. 环境的准备.

  1. node 这个不用说了吧, 前端必备
  2. git
  3. npm install -g yo generator-code 安装开发工具, 这个yo是帮助我们创建项目的, 你可以理解为cli
初始化项目

yo code 虽然很短小, 但没错就是这样的
image
这里需要我们选择初始化的工程, 这里我们主要聊插件部分, 并且ts并不是讨论的重点所以我们选择第二个JavaScript, 如果选第一个就会是使用ts开发插件.
image
输入一个插件名字, package.json中的displayName也会变成这个
image
是否也采用()内的名字, 项目名也是文件夹名, package.json中的name属性也会变成这个
image
输入对这个插件的描述, 项目名也是文件夹名, package.json中的description属性也会变成这个
image
在js文件中启动语义检测(可能用不到), jsconfig.json中compilerOptions.checkJs会变为true
image
是否初始化git仓库
image
选择钟爱的包管理方式

做完上述的步骤我们就得到了一个简易的工程

为了部分第一次做插件的同学入门, 这里暂时就不改动目录结构了, 下面介绍完配置我们在一起优化这个项目的结构.

三. 官网的第一个例子, 也许并不太适合入门时理解

extension.js 这个文件是初始项目时的入口文件, 我们的故事就是在这里展开的, 我们先把里面的注释都清理掉, 我来逐一解读一下这个基础结构.
初始化的里面有点乱, 大家可以把我下面这份代码粘贴进去, 看着舒爽多了.

const vscode = require('vscode');

function activate(context) {
    // 1: 这里执行插件被激活时的操作
    console.log('我被激活了!! 桀桀桀桀...')
    // 1: 定义了一个命令(vscode.commands)
    // 2: lulu.helloWorld 可以把它当做id
    let disposable = vscode.commands.registerCommand('lulu.helloWorld', function () {
        // 3: 触发了一个弹出框
        vscode.window.showInformationMessage('第一个demo弹出信息!');
    });
  // 4: 把这个对象放入上下文中, 使其生效
    context.subscriptions.push(disposable);
}

// 5: 插件被销毁时调用的方法, 比如可以清除一些缓存, 释放一些内存
function deactivate() {}

// 6: 忙活一大堆当然要导出
module.exports = {
    activate,
    deactivate
}
上面已经详细的标注了每一句代码, 接下来我们 按 F5 进入调试模式如图.

image

  1. 上方会出现一个操作栏, 我们接下来会经常与最后两个打交道
  2. 系统为我们新开了一个窗口, 这个窗口默认集成了我们当前开发的这个插件工程

image

下方也会出现调试台, 我们插件里面console.log打印的信息都会出现在这里.

官网这个例子需要我们 ctrl + shirt + p 调出输入框, 然后在里面输入hello w 就可以如图所示

image

用力狠狠敲击回车, 大喊神之一手, 响起bgm

image
在左下角就会出现这样一个弹出款, 这个例子也就完成了
image
组件显示被激活

那么我们一起来看一下, 这一套流程到底是怎么配置出来的!!
他的配置方式我比较不赞同, 都是在package.json里面进行的

package.json
image

activationEvents: 当什么情况下, 去激活这个插件, 也就是上面打印出桀桀怪笑.
activationEvents.onCommand: 在某个命令下激活(之后会专门列出很多其他条件)

定义命令

contributes.commands: 你可以理解为'命令'的列表
command: 命令的id (上面就是依靠这个id 找到这个命令)
title: 命令语句(可以看下图)
image
image

所以extension.js里面的registerCommand('lulu.helloWorld' 就是指定了, 这个命令Id 被执行的时候, 该执行的操作!
let disposable = vscode.commands.registerCommand('lulu.helloWorld', function () {
        // 3: 触发了一个弹出框
        vscode.window.showInformationMessage('第一个demo弹出信息!');
    });
之所以标题里说这个例子不是太好, 就是因为我们平时较少用vscode的命令去做某个操作, 并不会很生动的把我们带入进去

四. 项目结构微改 + 提示的类型

一个extension.js文件已经满足不了我们的'xie.nian'了, 所以我们要把它稍微改造一下.
老朋友src出场了, 毕竟我们以后要封装很多命令与操作, 工程化一点是必要的.

  1. 最外层新建src文件夹
  2. extension.js 改名 index.js, 放入src文件夹内.
  3. package.json中设置路径 "main": "./src/index.js"(重点入口文件)
  4. 新建“目录.md”文件, 把插件的简介完善一下, 保证任何时候最快速度理解项目。
  5. 新建message.js文件, 放置弹出信息代码.
index文件: 只负责导出引入各种功能, 不做具体的操作
const message = require('./message.js');

 function activate(context) {
    context.subscriptions.push(message);
}

module.exports = {
    activate
}
message.js 触发各种提示框
const vscode = require('vscode');

module.exports = vscode.commands.registerCommand('lulu.helloWorld', function () {
  vscode.window.showInformationMessage('第一个demo弹出信息!');
  vscode.window.showWarningMessage('第一个警告信息')
  vscode.window.showErrorMessage('第一个错误信息!');
});
目录.md(这个要随时更新不要偷懒)
# 目录

1. index 入口文件

2. message   提示信息插件

3. 

效果如下:

image.png

五. 激活的时机

出于性能的考虑, 我们的组件并不是任何时候都有效的, 也就是说不到对应的时机我们的组件处于未被激活的状态, 只有当比如说遇到js文件才会生效, 遇到scss文件才会生效, 只能某个命令才能生效等等情况.
package.jsonactivationEvents数组属性里面进行修改.
这里只是常用的类型, 具体的可以去官网文档查看.

    "activationEvents": [
        "onCommand:hello.cc",  // 执行hello命令时激活组件
        "onLanguage:javascript",  // 只有是js文件的时候激活组件(你可以做js的代码检测插件)
        "onLanguage:python", // 当时py文件的时候
        "onLanguage:json",
        "onLanguage:markdown",
        "onLanguage:typescript",
        "onDebug", // 这种生命周期的也有
        "*",
        "onStartupFinished"
    ],
  1. "*" 就是进来就激活, 因为他是任何时机(不建议使用这个).
  2. onStartupFinished他就友好多了,相当于但是他是延迟执行, 不会影响启动速度, 因为他是浏览器启动结束后调用(非要用""可以用这个代替).
这里有个坑点, 比如你有a,b 两个命令, 把a命令设为触发时机, 那么如果先执行b命令会报错, 因为你的插件还未激活.

六. 生命周期

与其他库一样, 生命周期是必不可少的(摘自官网).

七. window与mac的区别

我们知道, window与mac的案件是不大一样的, mac更多是使用command键, 这里我介绍一下分别设置快捷键.
`里面contributes`属性

"keybindings": [
 {
    "command": "hello.cc",
    "key": "ctrl+f10",
    "mac": "cmd+f10",
    "when": "editorTextFocus"
  }
],

八. when属性常用方式

接下来还有其他地方用到这个when属性那么这里就专门提一下吧
下面是比较常用的, 具体的要查官网, 毕竟太多了!

1. 编辑器获得焦点时
"when": "editorFocus"
2. 编辑器文本获得焦点
"when": "editorTextFocus"
3. 编辑器获取焦点并且四js文件的时候
"when": "editorFocus && resourceLangId == javascript"
4. 后缀不为.js
"when":"resourceExtname != .js"
5. 只在Linux,Windows环境下生效
"when": "isLinux || isWindows"
1. 要正确的理解when, 他不是字符串, 他是true 或 false的布尔, 写成字符串vscode会去解析的, 所以when可以直接传 true, false, 这里要注意, 是when: "true" when:"false"
2. 由第一条可知editorLangId其实就是运行时上下文的一个变量, 也就是文件类型名常量.

参考资料: https://code.visualstudio.com...

九. 所在的位置 左侧, 右上, 菜单的上与下

这里也只介绍最常用与好用的, 很多偏门知识学了我的这篇文章你也一定可以很轻易的自学了.
①. 鼠标右键
新建navigation.js文件用来展示我们的功能是否生效.
index.js里面引入

const vscode = require('vscode');
const message = require('./message.js');
const navigation = require('./navigation.js');

 function activate(context) {
    vscode.window.showInformationMessage('插件成功激活!');
    context.subscriptions.push(message);
    context.subscriptions.push(navigation);
}

module.exports = {
    activate
}
const vscode = require('vscode');

module.exports = vscode.commands.registerCommand('lulu.nav', function () {
  let day = new Date();
  day.setTime(day.getTime() + 24 * 60 * 60 * 1000);
  let date = day.getFullYear() + "-" + (day.getMonth() + 1) + "-" + day.getDate();
  vscode.window.showInformationMessage(`明天是: ${date}`);
});

上面是一个报告明天日期的功能, 当然我们可以在这里请求后端接口, 调取今天的人员安排列表之类的接口.

package.json里面的contributes属性中添加导航的配置.

1: 定义一个命令, 然后下面定义快捷触发这个命令

"commands": [
 {
  "command": "lulu.nav",
  "title": "点击我进行导航操作"
 }
],

2: 定义导航内容, 并且绑定点击事件

    "menus": {
     // 在编辑器的内容区域
        "editor/context": [
            {
                "when": "editorFocus", // 你懂得
                "command": "lulu.nav", // 引用命令
                "group": "navigation" // 放在导航最上方
            }
        ]
    }

image.png
image.png

②. 右上按钮
其实挺少有插件用这里的, 反而这里的市场没被占用, 想开发插件的同学可以抢先占领一下.

    "menus": {
        "editor/title": [
            {
                "when": "editorFocus", // 你懂得
                "command": "lulu.nav", // 引用命令
                "group": "navigation" // 放在导航最上方
            }
        ]
    }

image.png

③. 左侧导航栏, (这个我决定下面与tree一起讲, 因为那里是重点)

十. 加载的进度条(官网例子模糊)

加载是很常用的功能, 但是官网的例子也确实不够友好, 本着我踩过的坑就不希望别人踩的原则这里讲下进度条的实际用法.

老套路定义好命令

    {
                "command": "lulu.progress",
                "title": "显示进度条"
            }

为了方便 我们把它也定义在右键菜单里面

{
                "when": "editorFocus",
                "command": "lulu.progress",
                "group": "navigation"
            }

新建progress.js文件

const vscode = require('vscode');

module.exports = vscode.commands.registerCommand('lulu.progress', function () {
  vscode.window.withProgress({
    location: vscode.ProgressLocation.Notification,
    title: "载入xxxx的进度...",
    cancellable: true
  }, (progress) => {
    // 初始化进度
    progress.report({ increment: 0 });

    setTimeout(() => {
      progress.report({ increment: 10, message: "在努力。。。." });
    }, 1000);

    setTimeout(() => {
      progress.report({ increment: 40, message: "马上了..." });
    }, 2000);

    setTimeout(() => {
      progress.report({ increment: 50, message: "这就结束..." });
    }, 3000);

    const p = new Promise(resolve => {
      setTimeout(() => {
        resolve();
      }, 5000);
    });

    return p;
  })
});
  1. vscode.window.withProgress 第一个参数是配置, 第二个参数是操作
  2. vscode.ProgressLocation.Notification 来源信息, 让用户知道是哪个插件的进度条
  3. title 加载框标题
  4. cancellable 是否可取消
  5. progress.report 初始化进度
  6. message 拼在title后面的字符
  7. 因为返回的是promise, 所以规定调用 resolve则结束进度

image.png

进度条也算常用的基本功能, 好好用让自己的插件更友好.

可能是字数和图片有点多现在越写越卡, 只能跳到下篇去继续写啦.

lulu_up
5.7k 声望6.9k 粉丝

自信自律, 终身学习, 创业者