当前位置: 主页 > 程序 >

基于React的VSCode插件开发-创建工程

时间:2024-04-20  作者:haden   点击:
【摘要】# 0.背景## 0.1 VSCode ExtensionVSCode(全称:Visual Studio Code)是一款由微软开发且跨平台的免费源代码编辑器。该软件支持语法高亮、代码自动补全(又称 IntelliSense)、代码重构、查看定义功

0.背景

0.1 VSCode Extension

VSCode(全称:Visual Studio Code)是一款由微软开发且跨平台的免费源代码编辑器。该软件支持语法高亮、代码自动补全(又称 IntelliSense)、代码重构、查看定义功能,并且内置了命令行工具和 Git 版本控制系统。用户可以更改主题和键盘快捷方式实现个性化设置,也可以通过内置的扩展程序商店安装扩展以拓展软件功能。
得益于海量的优秀插件,VSCode可以用于各种语言、应用、场景的开发。在工作中,因为开发语言的特殊性,希望开发一款插件。因为VSCode UI是基于chromium,插件开发可以使用JavaScript或者微软推荐的TypeScript。
具体从零开始的插件开发流程,微软官方已经提供了很详细的教程:https://code.visualstudio.com/api/get-started/your-first-extension ,也有很多基于此的中文教程。这里不再从零介绍。
Webview API允许插件创建一个完全自定义的视图。比如自带的Markdown插件使用webview来渲染Markdown预览内容。Webview还可以用来创建VSCode原生不提供的复杂的用户接口。
具体代码更改,可以从https://github.com/sunhaox/vscode_extension_react_template 查看。

0.2 React

React是一个用于构建用户界面的JavaScript库,最初由Facebook于2011年开发,并于2013年5月开源。它起源于Facebook内部项目,用于改进Newsfeed和其他应用,如Instagram。React的设计目的是通过声明式设计、组件化和高效更新DOM来简化交互式UI的开发。
所以,我们的目的是,创建一个VSCode插件工程,并配置基于TypeScript的React工程,用于Webview的开发。

1.创建工程

1.1 VSCode插件工程创建

微软官方有详细的从零构建的文档,这里只列举用到的指令,不再做详细介绍。
安装过node.js和Yeoman后,开始创建工程。

必要的组件安装完成后,会问是否需要用VSCode打开工程,选择是,即可在vscode里编写、运行和调试插件了。

1.2 创建TypeScript的React工程

通过终端,进入刚才创建好的VSCode插件目录下,通过脚手架创建React工程:

cd vscode_extension_react_template
npx create-react-app webview --template typescript

等待配置完成。看到“Happy Hacking!”证明配置成功,我们可以进入webview下,通过npm run start来验证React工程。

1.3 修改工程以适配React

修改tsconfig.json,在分析vscode插件代码时,不包含webview内容,避免误报。

在webview工程里新建一个script文件夹,新建一个build-react-no-split.js文件。我们需要这个脚本帮我们生成react的最终结果,并修改命名,方便VSCode插件中调用。

#!/usr/bin/env node

/**
 * A script that overrides some of the create-react-app build script configurations
 * in order to disable code splitting/chunking and rename the output build files so
 * they have no hash. (Reference: https://mtm.dev/disable-code-splitting-create-react-app).
 * 
 * This is crucial for getting React webview code to run because VS Code expects a 
 * single (consistently named) JavaScript and CSS file when configuring webviews.
 */

const rewire = require("rewire");
const defaults = rewire("react-scripts/scripts/build.js");
const config = defaults.__get__("config");

// Disable code splitting
config.optimization.splitChunks = {
  cacheGroups: {
    default: false,
  },
};

// Disable code chunks
config.optimization.runtimeChunk = false;

// Rename main.{hash}.js to main.js
config.output.filename = "static/js/[name].js";

// Rename main.{hash}.css to main.css
config.plugins[5].options.filename = "static/css/[name].css";
config.plugins[5].options.moduleFilename = () => "static/css/main.css";

然后需要修改webview的package.json,在编译的时候调用脚本:

最后,在VSCode的插件工程里,修改package.json,方便我们在编译打包插件的时候一起生成webview内容。

2.插件里调用webview

2.1 启动webview

在插件里,通过createWebviewPanle()创建webview,并对其进行配置:

const panel = vscode.window.createWebviewPanel(
    'vscode-extension-react-template',
    'Webview Demo',
    vscode.ViewColumn.One,
    {
        enableFindWidget: true,
        enableScripts: true,
        localResourceRoots: [
            vscode.Uri.joinPath(context.extensionUri, "out"),
            vscode.Uri.joinPath(context.extensionUri, "webview/build")
        ],
        retainContextWhenHidden: true
    }
);

const lightIconPath = getFileUri(panel.webview, context.extensionPath, ["media", "icon-light.svg"]);
const darkIconPath = getFileUri(panel.webview, context.extensionPath, ["media", "icon-dark.svg"]);

panel.iconPath = {
    light: lightIconPath,
    dark: darkIconPath
};

const stylesUri = getUri(panel.webview, context.extensionUri, [
    "webview",
    "build",
    "static",
    "css",
    "main.css",
]);

const scriptUri = getUri(panel.webview, context.extensionUri, [
    "webview",
    "build",
    "static",
    "js",
    "main.js",
]);

const nonce = getNonce();

panel.webview.html=/*html*/ `
    <!DOCTYPE html>
    <html lang="en">
        <head>
            <meta charset="utf-8">
            <meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no">
            <meta name="theme-color" content="#000000">
            <meta http-equiv="Content-Security-Policy" content="default-src 'self' ; style-src 'unsafe-inline' ${panel.webview.cspSource}; script-src 'nonce-${nonce}';">

            <link rel="stylesheet" type="text/css" href="${stylesUri}">
            <title>Hello World</title>
        </head>
        <body>
            <noscript>You need to enable JavaScript to run this app.</noscript>
            <div id="root"></div>
            <script nonce="${nonce}" src="${scriptUri}"></script>
        </body>
    </html>
`;

这时,在wevbidw目录下,执行npm run build指令,编译react工程并重命名。
之后VSCode里按F5,在新的调试窗口里,按CTRL+SHIFT+P,执行Hello World指令,即可在vscode里看到我们之前创建的react webview内容。

2.2 webview和插件的通信

接下来,简单修改一下react工程,实现向插件发送消息。
首先引入@types/vscode-webview库,提供了webview和插件通信的接口。
然后额外引入一个@vscode/webview-ui-toolkit库,这个UI库提供了和VSCode风格相同的组件,并不是必须的。
修改react的App.tsx:

function App() {
  return (
    <div className="App">
      <VSCodeButton onClick={handleBtnClick}>
        Send Message To Extension
      </VSCodeButton>
    </div>
  );
}

function handleBtnClick() {
  const message: MessageFromWebview = {
    command: COMMAND.testMessageFromWebview,
    data: {
      message: "test message"
    },
  };
  vscode.postMessage(message);
}

之后我们再插件侧增加消息接收代码,在创建完panel后添加以下代码:

// handle the message from webview
panel.webview.onDidReceiveMessage(
    (message: Message) => {
        const command = message.command;

        switch (command) {
            case COMMAND.testMessageFromWebview:
                vscode.window.showInformationMessage(message.data.message);
                return;
        }
    }
);

因为修改了react代码,需要重新在webview目录下,运行npm run build编译。
此时,webview页面有一个按钮,点击后发送消息到插件侧,插件通过vscode接口在窗口右下角弹出一个提示窗口,显示webview发来的消息内容。

2.3 插件和webview的通信

接下来,我们实现插件侧向webview发送消息的功能。
先在插件的package.json里添加一条新的命令,然后在extension.ts里实现消息发送。

context.subscriptions.push(vscode.commands.registerCommand('vscode_extension_react_template.sendMessage', () => {
    const message: MessageFromExtension = {
        command: COMMAND.testMessageFromExtension,
        data: {
            message: "The message from extension."
        }
    };
    _panel?.webview.postMessage(message);

}));

之后修改React的App.tsx,增加一个VSCodeTextArea组件,用于显示接收到的插件发来的消息内容。

function App() {

  window.addEventListener('message', handleMessage);

  const [message, setMessage] = useState('');

  function handleMessage(event: MessageEvent) {
    const message = event.data as Message;
    switch (message.command) {
      case COMMAND.testMessageFromExtension: 
        setMessage(message.data.message);
        return;
    }
  }
  return (
    <div className="App">
      <VSCodeTextArea placeholder='will show the received message' readOnly value={message}>
      </VSCodeTextArea>
      <br/>
      <VSCodeButton onClick={handleBtnClick}>
        Send Message To Extension
      </VSCodeButton>
    </div>
  );
}

这时,通过CTRL+SHIFT+P执行命令Send Message To Webview,可以看到webview里的textArea内容发生更改。

3.发布准备

做完上述工作后,可以开始我们的插件和webview开发了。最后我们还需要一些额外的修改,来保证打包、发布的时候,只包含我们需要的文件。
我们使用vsce工具来打包发布,我们在插件的package.json文件里可以看到有vscode:prepublish字段,vsce工具会先调用该字段定义的脚本,完成预处理。这里,除了常规的插件的重新编译外,我们还需要工具完成React的编译优化,所以我们需要修改该字段:

  "scripts": {
    "install_webview": "cd webview && npm install",
    "install_all": "npm install && npm run install_webview",
    "build_webview": "cd webview && npm run build",
    "build_all": "npm run compile && npm run build_webview",
    "vscode:prepublish": "npm run build_all",
    "build": "npm run build_all",
    "compile": "tsc -p ./",
    "watch": "tsc -watch -p ./",
    "pretest": "npm run compile && npm run lint",
    "lint": "eslint src --ext ts",
    "test": "vscode-test"
  },

修改vscode:prepublishbuild字段内容。
完成编译后,工具会根据.vscodeignore里的内容,将指定内容外的文件打包至插件的安装包内。Webview目录下很多都是React的源文件和过程文件,我们只需要webview/build/static/里的最终编译产物。所以,在.vscodeigore里增加:

webview/src/**
webview/scripts/**
webview/public/**
webview/node_modules/**
webview/*.json
webview/*.md
webview/.gitignore
webview/*.json
webview/*.ico
webview/index.html
webview/robots.txt

至此,我们的包含React的VSCode插件工程就完全配置好了。

顶一下
(0)
0%
踩一下
(0)
0%
发表评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
验证码: 点击我更换图片

推荐内容