从零到一:VS Code 扩展开发全流程简介(含 Webview 与 React 集成)

作为曾经最受欢迎的编辑器,VS Code 的核心优势不仅体现在其原生功能的完备性,更源于其极为丰富的扩展生态系统。无论是基础的代码格式化、智能语法提示,还是针对特定开发场景的工具集成,扩展都能让 VS Code 突破原生能力边界,实现按需 "进化"。即便如今 AI 编辑器层出不穷,其中多数仍以 VS Code 内核为基础开发,扩展的作用依旧无可替代。

因此,本文将介绍如何从零开始开发一款属于自己的 VSCode 扩展。

前言 - 什么是 VSCode 扩展

如下是摘自 VS Code 官网的一段话,大致意思就是:

VS Code 在设计之初 就考虑到了可扩展性,从用户界面(UI)到编辑体验,VS Code 几乎所有功能模块都能通过扩展 API(Extension API)进行定制与增强。而且 VS Code 的诸多核心功能本身也是以扩展形式开发的,且同样基于这套扩展 API 实现。

Visual Studio Code is built with extensibility in mind. From the UI to the editing experience, almost every part of VS Code can be customized and enhanced through the Extension API. In fact, many core features of VS Code are built as extensions and use the same Extension API.

所以简单来说,VSCode 扩展是基于 VSCode 扩展 API 开发,用于扩展或定制 VSCode 编辑器功能的组件。

一、开发环境准备

  1. 安装 Node.js (建议版本 16+)

由于 VS Code 是基于 Electron 框架 开发的桌面应用,依赖于 Node.js 环境,所以在开发 VS Code 扩展之前要确保您已安装 Node.js

可在Node 官网 下载并安装,安装完成后执行如下命令确认安装是否成功:

bash 复制代码
node -v
  1. 安装 git

可在git 官网下载安装,主要用于扩展开发过程中的版本控制。

二、项目搭建

VS Code 官方提供了专门用于搭建扩展项目的脚手架: YeomanVS Code Extension Generator

  • 如果你不希望安装 Yeoman 和 VS Code Extension Generator,请在终端执行如下命令:
bash 复制代码
npx --package yo --package generator-code -- yo code
  • 如果您想全局安装 Yeoman 以方便重复运行,请在终端执行以下命令:
bash 复制代码
npm install --global yo generator-code

yo code

命令执行后会出现如下选项:

  • What type of extension do you want to create? :New Extension (TypeScript)
  • What's the name of your extension? :扩展名称(比如 hello-world-extension)。
  • What's the identifier of your extension? :扩展唯一标识(默认与名称一致即可)。
  • What's the description of your extension? :扩展描述(比如 My first VSCode extension)。
  • Initialize a git repository? :是否初始化 Git 仓库(选 No 也可以,后续自己手动初始化)。
  • Which bundler to use? :使用哪个打包工具(建议选 webpack)。
  • Which package manager to use? :使用哪个包管理工具(建议选 pnpm

所有选项配置完成后,待相关依赖下载安装完毕,扩展项目便搭建完成。接下来,用 VSCode 打开该项目文件夹,其结构如下

dart 复制代码
hello-world-extension/
├── .vscode
│   ├── launch.json     // Config for launching and debugging the extension
│   └── tasks.json      // Config for build task that compiles TypeScript
├── .gitignore          // Ignore build output and node_modules
├── README.md           // Readable description of your extension's functionality
├── src
│   └── extension.ts    // Extension source code
├── package.json        // Extension manifest
├── tsconfig.json       // TypeScript configuration

其中最关键的两个文件:package.json 和 extension.ts。

(1)package.json ------ 扩展清单

该文件是扩展的核心配置,VSCode 通过它识别扩展的功能。关键配置项如下:

json 复制代码
{
  "name": "hello-world-plugin",
  "displayName": "Hello World Plugin",
  "version": "0.0.1",
  "engines": {
    "vscode": "^1.80.0"  // 扩展支持的 VSCode 最低版本
  },
  "activationEvents": [  // 激活事件:当用户执行 "hello-world-plugin.helloWorld" 命令时激活
    "onCommand:hello-world-plugin.helloWorld"
  ],
  "contributes": {      // 贡献点:向 VSCode 注册命令
    "commands": [{
      "command": "hello-world-plugin.helloWorld",  // 命令唯一标识
      "title": "Hello World"                       // 命令在命令面板中的显示名称
    }]
  },
  "main": "./out/extension.js",  // 扩展编译后的入口文件(TypeScript 编译后输出到 out 文件夹)
  "scripts": {
    "vscode:prepublish": "npm run compile",  // 发布前执行的命令(编译 TypeScript)
    "compile": "tsc -p ./",                  // 编译 TypeScript 命令
    "watch": "tsc -watch -p ./",             // 监听文件变化,自动编译
    "pretest": "npm run compile && npm run lint",
    "lint": "eslint src --ext ts",
    "test": "node ./out/test/runTest.js"
  }
}

(2)extension.ts ------ 入口文件

该文件文件导出了两个函数:activatedeactivateactivate会在已注册的激活事件发生时执行。deactivate会让你在扩展停用前有机会进行清理。默认生成代码如下:

javascript 复制代码
// 导入 vscode 模块,获取 VSCode API
import * as vscode from 'vscode';
// 扩展激活时执行的函数(激活事件触发后调用)
export function activate(context: vscode.ExtensionContext) {
  // 注册一个命令:命令标识与 package.json 中一致
  let disposable = vscode.commands.registerCommand(
    'hello-world-extension.helloWorld', 
    () => {
      // 命令执行时的逻辑:弹出信息提示框
      vscode.window.showInformationMessage('Hello World!');
    }
  );
  // 将命令注册到上下文,确保扩展卸载时能释放资源
  context.subscriptions.push(disposable);
}
// 扩展卸载时执行的函数(可选,用于清理资源,比如关闭连接、删除临时文件)
export function deactivate() {}

有关 VSCode 扩展的开发流程以及相关文件的具体作用可参考如下官方文档介绍:

1\] [Your First Extension](https://link.juejin.cn?target=https%3A%2F%2Fcode.visualstudio.com%2Fapi%2Fget-started%2Fyour-first-extension "https://code.visualstudio.com/api/get-started/your-first-extension") \[2\] [Extension Anatomy](https://link.juejin.cn?target=https%3A%2F%2Fcode.visualstudio.com%2Fapi%2Fget-started%2Fextension-anatomy "https://code.visualstudio.com/api/get-started/extension-anatomy")

三、 运行与调试

我们可以通过如下步骤进行调试:

  1. 点击 VSCode 左侧的 运行和调试 图标(或按 Ctrl+Shift+D)。
  2. 点击顶部的 运行和调试 下拉框,选择 Run Extension
  3. 点击调试按钮(或F5)启动调试,会自动打开一个新的 VSCode 窗口(即 "扩展开发宿主" 窗口)。
  1. 然后在新窗口中,按 Ctrl+Shift+P 打开命令面板,输入并选择 Hello World 命令。
  2. 此时窗口右下角会弹出 Hello World from hello-world-extension! 的提示框 ------ 恭喜!你的第一个扩展运行成功了!

四、打包与发布

扩展开发完成后,可将扩展打包成 .vsix 文件,再发布到 VSCode 扩展市场,分享给其他人使用。对此 VS Code 官方提供了 vsce 工具。

vsce ( Visual Studio Code Extensions ) 是一款用于打包、发布和管理 VS Code 扩展的命令行工具

安装 vsce

bash 复制代码
npm install -g @vscode/vsce

4.1 打包(packaging)

安装好 vsce 工具后,在项目根目录执行如下命令,打包生成 .vsix 文件:

bash 复制代码
vsce package

打包成功后,项目根目录会生成一个 .vsix 文件(比如 hello-world-extension-0.0.1.vsix)。双击该文件,即可在 VSCode 中安装你的扩展。

4.2 发布到 VSCode 扩展市场

如果想向更多人分享你的扩展,可以发布到 VSCode 扩展市场,大致步骤如下:

  1. 登录到 Azure DevOps创建自己的组织
  2. 获取个人访问令牌(Personal Access Token)
  3. 创建发布者,并验证身份
  4. 在项目根目录执行如下命令,将扩展发布到市场
bash 复制代码
vsce publish

具体步骤详情可参考官方文档

至此,VS Code 扩展开发的全流程就结束了。但是在实际开发场景中,若想为用户提供更灵活、更复杂的交互界面,仅依靠 VS Code 原生 API 可能无法完全满足需求,此时 Webview 就成为了关键的进阶能力。

五、Webview

Webview API 允许扩展在 VS Code 中创建完全可定制的视图,可用于构建复杂的用户界面,超出 VS Code API 的支持范围。

5.1 Webview 开发示例

如下是官方文档中给出的一个有关 Webview API 使用的示例:

js 复制代码
import * as vscode from 'vscode';

export function activate(context: vscode.ExtensionContext) {
  context.subscriptions.push(
    vscode.commands.registerCommand('catCoding.start', () => {
      // 创建并显示 Webview Panel
      const panel = vscode.window.createWebviewPanel(
        'catCoding',  // viewType: Webview 面板的类型标识
        'Cat Coding',  // title: 面板的标题
        vscode.ViewColumn.One,  // column: 显示面板的编辑器列
        {} // options: Webview 选项
      );

      // 设置面板的 HTML 内容
      panel.webview.html = getWebviewContent();
    })
  );
}

function getWebviewContent() {
  return `<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Cat Coding</title>
</head>
<body>
    <img src="https://media.giphy.com/media/JIX9t2j0ZTN9S/giphy.gif" width="300" />
</body>
</html>`;
}

在扩展宿主窗口中,按 Ctrl+Shift+P 打开命令面板,输入并选择 Cat Coding 命令,会显示如下视图:

简单来说,可以将 webview 视为 VS Code 中由扩展控制的 iframewebview 可以在该框架中呈现几乎任何 HTML 内容,并通过消息传递(PostMessage)与扩展进行通信。

有关如何使用 Webview API 创建自定义视图,以及 Webview 如何与扩展进行通信,可参考官方文档的介绍。

5.2 基于 React 开发 Webview

如上述所示,webview 的内容是通过 HTML 代码来定义的。但是如果直接编写原生 HTML 开发效率低且难以维护。目前前端项目通常基于 ReactVue 等框架来开发。那我们是否也可以基于这些框架来开发 webview 呢?答案是肯定的!!!

我们可以通过如下步骤在扩展项目中集成一个 React 项目,用于 webview 的开发:

1. 创建 React 子项目

在扩展项目 src 目录下创建一个 react-webview(命名可自定义) 文件夹。

然后切换到react-webview目录下,使用 Vite 快速创建一个 React 项目

bash 复制代码
// 切换到 react-webview 目录
cd src/react-webview

// 创建 React 项目
pnpm create vite@latest . --template react-ts

//安装依赖
pnpm install

注:本文使用 Vite 创建 React 项目,使用其他脚手架或者自己搭建也可以。除了后续打包配置上有区别,大致流程差不多。

2. 修改打包配置

编辑 react-webview/vite.config.ts,主要修改以下内容:

  • 配置 build.outDir:指定输出目录(建议放在插件项目的 dist 文件夹下,便于管理)。
  • 使用 vite-plugin-style-inject 插件,将 CSS 注入 JS 文件中。(便于后续HTML代码中只需引入一个文件)

修改后的配置如下:

ts 复制代码
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import styleInject from 'vite-plugin-style-inject';

export default defineConfig({
  plugins: [
    react(), 
    styleInject() // 将 CSS 直接注入到 JS 中
  ],
  build: {
    // 打包输出到插件项目的 dist/react-webview 目录
    outDir: '../../dist/react-webview',
    // 确保静态资源文件名稳定(避免哈希值导致路径变化)
    assetsDir: 'assets',
    rollupOptions: {
      output: {
        // 固定 chunk 文件名(可选,便于调试)
        chunkFileNames: 'assets/[name].js',
        entryFileNames: 'assets/[name].js',      
      },
    },
  },
});

配置修改完成后,在 react-webview 目录下运行打包命令:

bash 复制代码
pnpm run build

打包完成后,插件项目根目录会生成 dist/react-webview 文件夹,包含 index.htmlassets/index.js 等文件。

3. Webview 中引入打包后的 js 文件

打包完成后,我们只需在 HTML 代码中通过<script>标签引入打包生成的 dist/react-webview/assets/index.js 文件即可,具体代码如下:

ts 复制代码
import * as vscode from 'vscode';
import * as path from 'path';

export function activate(context: vscode.ExtensionContext) {
  // 1. 注册命令:触发 Webview 打开(命令名需与 package.json 匹配)
  let disposable = vscode.commands.registerCommand(
    'hello-world-extension.openReactWebview',
    () => {
      // 2. 创建 Webview 面板
      const panel = vscode.window.createWebviewPanel(
        'reactWebview', // Webview 唯一标识
        'React Webview Demo', // 面板标题
        vscode.ViewColumn.One, // 打开的列(默认第一列)
        {
          // 3. 配置 Webview 选项(关键:允许访问本地资源)
          enableScripts: true, // 允许执行 JS
          localResourceRoots: [
            // 配置允许访问的本地资源路径(React 打包后的 dist 目录)
            vscode.Uri.file(path.join(context.extensionPath, 'dist/react-webview'))
          ]
        }
      );
      // 4. 生成 React 资源的 URI(将本地路径转为 Webview 可访问的 URI)
      const fileUri = vscode.Uri.file(
        path.join(context.extensionPath, 'dist/react-webview', 'assets/index.js')
      );
      // 关键:使用 webview.asWebviewUri() 转换路径,避免安全限制
      const scriptUri = panel.webview.asWebviewUri(fileUri);

      panel.webview.html = getWebviewContent(scriptUri);
    }
  );
  context.subscriptions.push(disposable);
}

export function deactivate() { }

function getWebviewContent(scriptUri: vscode.Uri): string {
  return `<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>React Webview</title>
    <script type="module" crossorigin src="${scriptUri}"></script>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>`
}

需要注意的是 :HTML 代码中需要包含 <div id="root"></div> 元素,这个 React 页面的根元素。

4. 配置插件命令(package.json)

编辑插件根目录的 package.json,添加命令注册和激活事件,确保插件能被触发:

json 复制代码
{
  "contributes": {
    "commands": [
      {
        "command": "hello-world-extension.openReactWebview",
        "title": "Open React Webview",
        "category": "Hello World Extension"
      }
    ]
  },
}

运行和调试

完成上述步骤后,即可启动插件,在命令面板中选择 Open React Webview 命令,会显示如下视图:

如上步骤所示,我们需要先打包 React 子项目打包,然后再 Webview 的 HTML 代码中引入打包生成的文件。如果 React 子项目中代码发生变化,那么每次调试之前都需要重新打包,这种开发体验是不好的。对此,我们可以通过以下方式实现开发模式下的热重载(HMR)

使用 watch 模式构建,即在执行 build 命令时添加 --watch 参数:

bash 复制代码
pnpm run build --watch

:有关如何基于 React 开发 webview,本文只是给出了大体步骤,但是具体实现细节会根据所选择的工具不同而有所差异。

但是大体思想就是将 React 项目打包成一个 js 文件,然后再 Webview 的 HTML 代码中引入这个文件。

总结

本文简单介绍了 VS Code 扩展开发全流程,同时针对复杂界面需求,补充了 Webview 基础使用及集成 React 框架的进阶方案。如有错误,欢迎大佬们指正。

VS Code Extension API

相关推荐
起风了啰10 小时前
SkySwitch 云控灯
前端
Deepsleep.10 小时前
前端常见安全问题 + 防御方法 + 面试回答
前端·安全·面试
IT小农工10 小时前
windows系统edge浏览器退出账户后还能免密登录
前端·edge
柯南952710 小时前
Chrome浏览器插件(Extensions)的原理
前端·chrome
小妖66610 小时前
如何去除edge浏览器的灰色边框
前端·edge
猪哥帅过吴彦祖10 小时前
JavaScript Set 和 Map:现代 JavaScript 的数据结构双雄
前端·javascript·面试
用户03048059126310 小时前
null 与 undefined 的区别
前端
朱程10 小时前
写给自己的 LangChain 开发教程(四):RAG(1)
前端·人工智能
alphardex10 小时前
现代 Web 的视觉组件探索
前端·html·web components