记一次前端"vscode插件编写实战"超详细的分享会(上)
记录了我在组内的技术分享, 有同样需求的同学可以参考一下
分享全程下来时间大约 77分钟
一. vscode我们的战友
vscode本身都是用浏览器实现的, 使用的技术为Electron, 可以说vscode本身对我们前端工程师很友好, 在vscode上有很多优秀的插件供开发者使用, 那你有没有想过开发一款适合自己团队使用的插件?
写文章之前我在网上找了很多资料, 但视频方面的资料太少了, 官方网站也并没有被很好很完整的汉化, 文章还是有一些的但大多举的例子不好运行, 需要读者自己配置一堆东西, 官网到是提供了再github上的一套例子, 但都是用ts编写并且部分运行都有问题, 里面的思维逻辑与书写方式也不适合入门学习, 所以我才在此写一篇更加易懂的分享教程, 希望可以让刚入门的同学更好的学习起来, 话不多说我们一步步深入探索.
二. 环境的准备.
- node 这个不用说了吧, 前端必备
- git
- npm install -g yo generator-code 安装开发工具, 这个yo是帮助我们创建项目的, 你可以理解为cli
初始化项目
yo code
虽然很短小, 但没错就是这样的
这里需要我们选择初始化的工程, 这里我们主要聊插件部分, 并且ts并不是讨论的重点所以我们选择第二个JavaScript
, 如果选第一个就会是使用ts开发插件.
输入一个插件名字, package.json中的displayName也会变成这个
是否也采用()内的名字, 项目名也是文件夹名, package.json中的name属性也会变成这个
输入对这个插件的描述, 项目名也是文件夹名, package.json中的description属性也会变成这个
在js文件中启动语义检测(可能用不到), jsconfig.json中compilerOptions.checkJs会变为true
是否初始化git仓库
选择钟爱的包管理方式
做完上述的步骤我们就得到了一个简易的工程
为了部分第一次做插件的同学入门, 这里暂时就不改动目录结构了, 下面介绍完配置我们在一起优化这个项目的结构.
三. 官网的第一个例子, 也许并不太适合入门时理解
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
进入调试模式如图.
- 上方会出现一个操作栏, 我们接下来会经常与最后两个打交道
- 系统为我们新开了一个窗口, 这个窗口默认集成了我们当前开发的这个插件工程
下方也会出现调试台, 我们插件里面console.log打印的信息都会出现在这里.
官网这个例子需要我们 ctrl + shirt + p
调出输入框, 然后在里面输入hello w
就可以如图所示
用力狠狠敲击回车, 大喊神之一手
, 响起bgm
在左下角就会出现这样一个弹出款, 这个例子也就完成了
组件显示被激活
那么我们一起来看一下, 这一套流程到底是怎么配置出来的!!
他的配置方式我比较不赞同, 都是在package.json里面进行的
package.json
activationEvents
: 当什么情况下, 去激活这个插件, 也就是上面打印出桀桀怪笑.activationEvents.onCommand
: 在某个命令下激活(之后会专门列出很多其他条件
)
定义命令
contributes.commands
: 你可以理解为'命令'的列表command
: 命令的id (上面就是依靠这个id 找到这个命令)title
: 命令语句(可以看下图)
所以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
出场了, 毕竟我们以后要封装很多命令与操作, 工程化一点是必要的.
- 最外层新建src文件夹
extension.js
改名index.js
, 放入src文件夹内.package.json
中设置路径 "main": "./src/index.js"(重点入口文件)- 新建“目录.md”文件, 把插件的简介完善一下, 保证任何时候最快速度理解项目。
- 新建
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.
效果如下:
五. 激活的时机
出于性能的考虑, 我们的组件并不是任何时候都有效的, 也就是说不到对应的时机我们的组件处于未被激活的状态, 只有当比如说遇到js
文件才会生效, 遇到scss
文件才会生效, 只能某个命令才能生效等等情况.
在package.json
的activationEvents数组
属性里面进行修改.
这里只是常用的类型, 具体的可以去官网文档查看.
"activationEvents": [
"onCommand:hello.cc", // 执行hello命令时激活组件
"onLanguage:javascript", // 只有是js文件的时候激活组件(你可以做js的代码检测插件)
"onLanguage:python", // 当时py文件的时候
"onLanguage:json",
"onLanguage:markdown",
"onLanguage:typescript",
"onDebug", // 这种生命周期的也有
"*",
"onStartupFinished"
],
- "*" 就是进来就激活, 因为他是任何时机(不建议使用这个).
onStartupFinished
他就友好多了,相当于但是他是延迟执行, 不会影响启动速度, 因为他是浏览器启动结束后调用(非要用""可以用这个代替).
这里有个坑点, 比如你有a,b 两个命令, 把a命令设为触发时机, 那么如果先执行b命令会报错, 因为你的插件还未激活.
六. 生命周期
与其他库一样, 生命周期是必不可少的(摘自官网).
onLanguage
onCommand
workspaceContains
onFileSystem
onView
onUri
onWebviewPanel
onCustomEditor
*
onStartupFinished
七. 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" // 放在导航最上方
}
]
}
②. 右上按钮
其实挺少有插件用这里的, 反而这里的市场没被占用, 想开发插件的同学可以抢先占领一下.
"menus": {
"editor/title": [
{
"when": "editorFocus", // 你懂得
"command": "lulu.nav", // 引用命令
"group": "navigation" // 放在导航最上方
}
]
}
③. 左侧导航栏, (这个我决定下面与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;
})
});
vscode.window.withProgress
第一个参数是配置, 第二个参数是操作vscode.ProgressLocation.Notification
来源信息, 让用户知道是哪个插件的进度条title
加载框标题cancellable
是否可取消progress.report
初始化进度message
拼在title后面的字符- 因为返回的是promise, 所以规定调用
resolve
则结束进度
进度条也算常用的基本功能, 好好用让自己的插件更友好.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。