如何用 vue3 开发 IDE 插件
简介
当你想写 IDE 插件的时候,是否觉得原生的写法太过麻烦,为什么不能用框架来写?当 webview 里的页面有十分复杂的交互的时候,写插件里的页面就变成了一件十分头疼的事,任凭外面的框架如何花里胡哨,你也只能使用原生的写法来实现,本文将介绍如何把 Vue3 运用到插件里来开发。
准备工作
创建插件
-
全局安装
yo
和generator-code
:npm install -g yo generator-code
-
按照步骤,新建插件,选择
Typescript
来开发插件:yo code _-----_ ╭──────────────────────────╮ | | │ Welcome to the Visual │ |--(o)--| │ Studio Code Extension │ `---------´ │ generator! │ ( _´U`_ ) ╰──────────────────────────╯ /___A___\ / | ~ | __'.___.'__ ´ ` |° ´ Y ` ? What type of extension do you want to create? New Extension (TypeScript) ? What's the name of your extension? helloWorld ? What's the identifier of your extension? helloworld ? What's the description of your extension? ? Initialize a git repository? No ? Bundle the source code with webpack? Yes ? Which package manager to use? npm ❯ yarn
新建 vue 项目
-
进入插件目录,新建一个名为 frontend 的 vue 项目,需要先安装 vue cli。
vue create frontend
-
此时,你会发现,这里的 vue 项目还是 js 的,我们把它换成 ts 的。
```shell vue add typescript ```
注意: 其中的
Convert all .js files to .ts
选项选 Y。
新建插件要打开的 panel
新建 TemplatePanel 类
// TemplatePanel.ts
export class TemplatePanel {
private static currentPanel: TemplatePanel | undefined;
private readonly _panel: vscode.WebviewPanel;
private readonly _context: vscode.ExtensionContext;
private readonly _extensionPath: string;
private _disposables: vscode.Disposable[] = [];
private constructor(
panel: vscode.WebviewPanel,
context: vscode.ExtensionContext
) {
this._panel = panel;
this._context = context;
this._extensionPath = context.extensionPath;
this.initialize();
}
private async initialize() {
this._panel.onDidDispose(() => this.dispose(), null, this._disposables);
}
public static show(context: vscode.ExtensionContext) {
// 如果打开了页面,不重复打开
if (TemplatePanel.currentPanel) {
const column = vscode.window.activeTextEditor
? vscode.window.activeTextEditor.viewColumn
: undefined;
TemplatePanel.currentPanel._panel.reveal(column);
return;
}
const panel = vscode.window.createWebviewPanel(
"Template",
localize("template"),
vscode.ViewColumn.Active,
{
enableScripts: true,
retainContextWhenHidden: true,
// 用于 IDE 自带的文件协议的可读路径
localResourceRoots: [
vscode.Uri.file(path.join(context.extensionPath, "media")),
vscode.Uri.file(path.join(context.extensionPath, "frontend", "dist")),
],
}
);
TemplatePanel.currentPanel = new TemplatePanel(panel, context);
}
public dispose() {
TemplatePanel.currentPanel = undefined;
this._panel.dispose();
while (this._disposables.length) {
const x = this._disposables.pop();
if (x) {
x.dispose();
}
}
}
}
如何获取 vue 的前端页面
- 读取 vue 项目打包后的文件,并把引用的文件路径替换成 IDE 能够识别的文件协议格式。
async function getWebviewContent(extensionPath: string) {
const distPath = vscode.Uri.file(
path.join(extensionPath, "frontend", "dist")
).with({ scheme: "vscode-resource" });
// 读取 dits 下的 index.html
let html = await fse.readFile(
path.join(__dirname, "../frontend/dist/index.html")
);
const hrefReg = /href=(["']{1})\/{1}([^\/])/gi;
const srcReg = /src=(["']{1})\/{1}([^\/])/gi;
// 把文件路径替换成 IDE 能够识别的文件协议格式
let str = html
.toString()
.replace(hrefReg, `href=$1${distPath}/$2`)
.replace(srcReg, `src=$1${distPath}/$2`);
return str;
}
- 在 initialize 方法中赋值 webview 的 html:
// TemplatePanel.ts 中的 initialize 方法
this._panel.webview.html = await getWebviewContent(this._extensionPath);
建立插件端和前端页面(Vue)的通信
vue 编写的页面相当于是前端(浏览器端),而插件端的逻辑相当于是后端(服务器端),两者需要通信,那怎么才能在不启动服务的情况下进行通信呢,这就要用到 postMessage 了。
- 在前端页面中,增加 message 的监听:
// main.ts
window.addEventListener("message", (event) => {
const message = event.data;
switch (message.command) {
case "setMessage": {
break;
}
default:
break;
}
});
- 在插件端,也要增加 message 的监听,我们写在 initialize 方法里:
this._panel.webview.onDidReceiveMessage(
async (message) => {
switch (message.command) {
case "getMessage":
break;
default:
break;
}
},
null,
this._disposables
);
- 前端向后端发送消息:
// main.ts
// 引入 acquireVsCodeApi 的方法
const ide = acquireVsCodeApi();
// 发送消息
ide.postMessage({
command: "getMessage",
});
- 后端向前端发送消息:
// templatePanel.ts
this._panel.webview.postMessage({
command: "setMessage",
data: {
message: "hello",
},
});
新增 Vuex
- 安装 vuex,由于我们用的是 vue3,所以要安装
vuex@next
yarn add vuex@next --save
- 在 store.ts 中初始化 vuex
// store.ts
import { createStore } from "vuex";
export const store = createStore({
state: {},
modules: {},
});
- 在 mian.ts 中引入 vuex
// main.ts
import { store } from "./store";
const app = createApp(App);
app.use(store);
app.mount("#app");
新增多语言(vue-i18n)
- 安装 vue-i18n,由于我们用的是 vue3,所以要安装
vue-i18n@next
yarn add vue-i18n@next --save
- 在 i18n.ts 中初始化
// i18n.ts
import { createI18n } from "vue-i18n";
const en = require("./locale/en.json");
const zhCN = require("./locale/zh-CN.json");
// 语言包根据语言环境分类
const messages = {
en,
"zh-CN": zhCN,
};
const i18n = createI18n({
locale: "zh-CN", // 设置当前语言环境,默认中文简体
messages, // 设置语言环境对应信息
});
export default i18n;
- 在 mian.ts 中引入 vue-i18n
// main.ts
import i18n from "./i18n/index";
const app = createApp(App);
app.use(i18n);
app.mount("#app");
- 可以在代码中切换语言:
this.$i18n.locale = this.language;
如何在 vue 项目中使用静态文件
由于在 IDE 的 webview 中,使用的是 IDE 的文件协议,而不是正常的 file:// 协议。当你直接使用相对路径或者绝对路径时,是找不到静态资源的,那么,要如何才能在 vue 里用上静态资源呢,方法也很简单,只需要在 vue 项目用上 IDE 的文件协议头即可,我们这里把静态资源放在 media 目录下。
- 获取文件协议的 uri
// 获取文件协议的 uri
this._panel.webview.asWebviewUri(
vscode.Uri.file(path.join(this._extensionPath, "media"))
- 通过之前的通信方式,把 uri 传到前端页面。
this._panel.webview.postMessage({
command: "setSetting",
data: {
resource: this._panel.webview.asWebviewUri(
vscode.Uri.file(path.join(this._extensionPath, "media"))
),
},
- 前端接收之后把 uri 拼接成文件协议头:
const resource =
message.data.resource.scheme +
"://" +
message.data.resource.authority +
message.data.resource.path;
在页面中使用:
<img :src="`${resource}/images/logo.png`" alt="" />
这样就可以在前端页面中使用静态资源了。
如何调试
watch vue 项目内容
在 frontend 目录下的 package.json 中配置命令:
"scripts": {
"watch": "vue-cli-service build --watch"
}
配置完成之后,运行 yarn watch,即可实时监听代码变化,无需重新 build。
watch 插件中内容
由于在创建插件模板的时候,已经自行创建了 watch 命令,所以只需要在项目的根目录运行 yarn watch
即可。
运用 vscode 调试
-
点击侧边栏的「运行和调试」,运行 Run Extension 任务。
-
运行之后,会打开一个新的窗口,使用快捷键
mac:cmd + shift + p
win:ctrl + shift + p
,输入show panel
,即可打开之前写的页面。
总结
本文介绍了如何用 vue3 来开发 IDE 插件,融合了 vuex、typescript、vue-i18n 等,能够满足日常的开发,如需查看更多的插件开发内容,可查看 文档,有丰富的 api 能够使用。
附录
项目地址 https://github.com/vivoquickapp/vue3-ide-extension-template