每天在 VSCode 里待八小时,总有些重复动作让人手痒:查个正则、转个时间戳、格式化一段 JSON,每次都要切浏览器找在线工具。趁着元旦假期最后一天,我照着官方文档把 VSCode 插件开发从头走了一遍,做了个前端工具箱塞进侧边栏,顺手把整个流程记下来。这篇从装脚手架讲到发布上架,中间把激活事件、贡献点、Webview 通信这几个绕人的概念拆开说,代码都是能直接跑的。看完你至少能把自己那几个小工具做成插件,不用再开浏览器。

在 VSCode 插件市场里搜「前端工具箱」,或者直接打开 https://marketplace.visualstudio.com/items?itemName=poetries.fe-tools 就能装。
在本篇文章中,我们将从浅入深,和大家一起学习以下知识:
- 开发环境怎么搭,
yo code脚手架问的那几个问题该怎么答 - 按 F5 之后发生了什么,扩展宿主窗口和编辑窗口的关系
- 怎么给插件打断点,Webview 里的代码又该怎么调
- 激活事件、贡献点、VS Code API 这三个概念各自管什么
package.json清单文件的每个字段是干嘛的extension.js里activate和deactivate的调用时机- Webview 面板怎么创建,本地资源路径为什么不能直接写
- Webview 和插件之间怎么双向通信
- vsce 打包、发布、升级的完整命令,以及一份发布前 checklist
# 一、Step 1 搭开发环境
先把环境准备好。我用的是 macOS,第一步确认 VS Code、Node.js 和 Git 都装了:
code -v
node -v
npm -v
git --version
code -v 如果报命令找不到,去 VS Code 里按 Cmd+Shift+P 执行 Shell Command: Install 'code' command in PATH,这一步在后面本地安装 vsix 的时候还会用到。
官方的起步文档在这里,建议对照着看:https://code.visualstudio.com/api/get-started/your-first-extension
然后全局装脚手架。yo 是 Yeoman,generator-code 是 VS Code 官方提供的插件项目生成器:
npm install -g yo generator-code
用 yo code 初始化项目,它会问你一串问题:
yo code
# What type of extension do you want to create?
# 创建哪一种类型的扩展
# What's the name of your extension?
# 扩展的名称
# What's the identifier of your extension?
# 扩展的标识
# What's the description of your extension?
# 扩展的描述
# Initialize a git repository?
# 是否初始化 git 仓库
# Which package manager to use?
# 使用哪一种包管理器
第一个问题最关键,它决定了生成什么骨架。当年的选项主要是 JavaScript、TypeScript、Color Theme、Language Support、Code Snippets、Keymap、Extension Pack 这几类,选 JavaScript 或 TypeScript 就能拿到一个可运行的 helloworld 项目。
这里补一句时效性的话。这篇写于 2021 年初,yo code 的选项列表这几年增加过(比如面向浏览器环境的 Web Extension 类型),提问的措辞也调整过。你跑出来和上面对不上是正常的,按提示选就行,具体以官方文档为准。标识(identifier)这一项建议一次填对,它会和后面的发布者名字拼成插件在市场里的唯一 ID,改起来很麻烦。
# 二、Step 2 跑起来,然后学会调试
# 按 F5 之后发生了什么
用 VS Code 打开刚生成的项目,在编辑器里按 F5,它会编译并打开一个新窗口,官方叫「扩展开发宿主机」(Extension Development Host)。为了叙述方便,下面把新打开的窗口叫运行窗口,原来那个叫编辑窗口。
在运行窗口的命令面板(Ctrl+Shift+P,macOS 是 Cmd+Shift+P)里执行 Hello World 命令,右下角就会弹出通知。到这一步,你已经跑起了一个自己写的插件。

这个两窗口的设计一开始会有点绕,但想明白就很自然:插件跑在一个独立的扩展宿主进程里,编辑窗口是你的开发环境,运行窗口是插件的沙箱。你改了代码,在运行窗口按 Cmd+R 重载就能生效,不用重启整个 VS Code。
# 打断点调试
调试插件很省事,不需要额外配置,yo code 生成的项目里已经带好了 .vscode/launch.json。
在编辑窗口打开 extension.js,点击行号左侧的边栏设置断点。然后在运行窗口的命令面板里执行 Hello World 命令,断点就会命中,变量、调用栈、单步执行全都能用,和调试普通 Node 程序一模一样。

这块要理解的是:插件代码跑在 Node 环境里,所以你调的是 Node 进程,console.log 的输出会打到编辑窗口的「调试控制台」,不是运行窗口的开发者工具。这两个地方我一开始经常找错。
# 调试 Webview
Webview 是另一回事。它其实就是一个内嵌的网页,跑在渲染进程里,Node 的断点管不着它。
调试方法是这样:按 F5 打开调试模式,在 Webview 页面上按 Cmd+Shift+P,执行 Open Webview Developer Tools(面板里搜 webview 就能找到)。

打开之后就是一个完整的 Chrome DevTools,Elements、Console、Network、Sources 全都在,调它和调普通网页没有区别。

所以一个带 Webview 的插件其实有两套调试链路,插件侧走 Node 断点,Webview 侧走 DevTools。搞混了会浪费很多时间,这个我踩过,在 Webview 里打 console.log 死活在调试控制台看不到,排查了一下午才反应过来看错地方了。
# 三、Step 3 读懂 helloworld 这个骨架
接下来深入看看 helloworld 插件。它的功能极简,用户在命令面板执行 Hello World 命令,弹出一条 Hello World 消息。
从实现角度看,它做了三件事:
- 注册激活事件
onCommand:extension.helloWorld,插件在这个命令被触发时才被激活 - 注册贡献点
contributes.commands:extension.helloWorld,在命令面板中登记 Hello World 命令,并把它绑到extension.helloWorld这个 ID 上 - 调用 VS Code API
commands.registerCommand,给这个命令 ID 绑定真正的处理函数
# 三个基本概念
上面提到的激活事件、贡献点和 API,是插件开发里绕不开的三个词,值得单独说清楚。
激活事件(Activation Events),在配置清单 package.json 里静态声明,就是 JSON 数组 activationEvents 的值。当声明的事件发生时,插件才会被激活。当年可用的激活事件包括 onLanguage、onCommand、onDebug、onDebugInitialConfigurations、onDebugResolve、workspaceContains、onFileSystem、onView、onUri、onWebviewPanel,以及表示「启动就激活」的 *。
那为什么要有激活事件这个东西?因为 VS Code 不想在启动时加载你的插件。一个人装二三十个插件很常见,如果每个都在启动时跑一遍初始化,编辑器几秒钟都起不来。所以设计成按需激活:声明清楚你在什么情况下需要被叫醒,其余时间你的代码根本不会被加载。别图省事写 *,那等于在所有人的启动时间上收税。
这里也有个时效性的提醒。较新版本的 VS Code 对 contributes.commands 里声明过的命令会自动推导出对应的激活事件,不用再在 activationEvents 里重复写一遍 onCommand:xxx。你如果看到新生成的项目里 activationEvents 是个空数组,不是脚手架坏了。具体规则以官方文档为准。
贡献点(Contribution Points),同样在 package.json 里静态声明,指的是 VS Code 开放给插件扩展的那些功能点。当年可用的贡献点包括 configuration、configurationDefaults、commands、menus、keybindings、languages、debuggers、breakpoints、grammars、themes、snippets、jsonValidation、views、viewsContainers、problemMatchers、problemPatterns、taskDefinitions、colors、typescriptServerPlugins、resourceLabelFormatters 等。
理解贡献点的关键是:它们全是声明式的。你在 JSON 里说「我要往右键菜单加一项」「我要占一个侧边栏图标」,VS Code 读了清单就知道该怎么渲染 UI,压根不用先加载你的代码。这也是它敢做懒激活的前提。
VS Code API,一组能在扩展代码里调用的 JavaScript 接口。官方文档列了全部可用 API,熟悉常用的那些就够了(window、commands、workspace、languages 这几个命名空间覆盖了绝大多数场景),其他的用到再查。
一句话串起来:贡献点负责「让用户看见入口」,激活事件负责「决定什么时候加载你」,API 负责「加载之后你能干什么」。
# JavaScript 插件的目录结构

目录结构很简单,看名字大概就知道作用。不同项目类型的结构差别可能很大,但对 JavaScript 项目来说,最重要的就两个文件:package.json 和 extension.js。前者告诉 VS Code「我是谁、我提供什么」,后者是真正跑起来的代码。
# 清单文件 package.json
每个 VS Code 插件都必须有一个描述自己的清单文件 package.json。它是声明式的 JSON,用来声明插件名(name)、展示名(displayName)、描述(description)、版本(version)、引擎(engines)、类别(categories)、依赖(devDependencies)、脚本(scripts)、贡献点(contributes)、入口文件(main)、激活事件(activationEvents)等。
下面是 helloworld 插件的清单内容,我加了注释方便对照: