前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版

初探vscode插件开发 从环境搭建到发布上架

首页2021-01-03 15:01:24Front-End
插件vscode工程化

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

前端工具箱插件在 VSCode 中的运行截图

在 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 命令,右下角就会弹出通知。到这一步,你已经跑起了一个自己写的插件。

按 F5 打开扩展开发宿主窗口并执行 Hello World 命令的演示动图

这个两窗口的设计一开始会有点绕,但想明白就很自然:插件跑在一个独立的扩展宿主进程里,编辑窗口是你的开发环境,运行窗口是插件的沙箱。你改了代码,在运行窗口按 Cmd+R 重载就能生效,不用重启整个 VS Code。

# 打断点调试

调试插件很省事,不需要额外配置,yo code 生成的项目里已经带好了 .vscode/launch.json。

在编辑窗口打开 extension.js,点击行号左侧的边栏设置断点。然后在运行窗口的命令面板里执行 Hello World 命令,断点就会命中,变量、调用栈、单步执行全都能用,和调试普通 Node 程序一模一样。

在 extension.js 中设置断点并命中的调试演示动图

这块要理解的是:插件代码跑在 Node 环境里,所以你调的是 Node 进程,console.log 的输出会打到编辑窗口的「调试控制台」,不是运行窗口的开发者工具。这两个地方我一开始经常找错。

# 调试 Webview

Webview 是另一回事。它其实就是一个内嵌的网页,跑在渲染进程里,Node 的断点管不着它。

调试方法是这样:按 F5 打开调试模式,在 Webview 页面上按 Cmd+Shift+P,执行 Open Webview Developer Tools(面板里搜 webview 就能找到)。

命令面板中查找 webview 开发者工具的入口

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

Webview 开发者工具打开后的界面,可以像调试网页一样调试 Webview 内容

所以一个带 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 插件的目录结构

VS Code 插件项目的目录结构,包含 package.json、extension.js 与 test 目录

目录结构很简单,看名字大概就知道作用。不同项目类型的结构差别可能很大,但对 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 插件的清单内容,我加了注释方便对照:

fe
  • 一、Step 1 搭开发环境
  • 二、Step 2 跑起来,然后学会调试
    • 按 F5 之后发生了什么
    • 打断点调试
    • 调试 Webview
  • 三、Step 3 读懂 helloworld 这个骨架
    • 三个基本概念
    • JavaScript 插件的目录结构
    • 清单文件 package.json
    • 插件入口文件 extension.js
  • 四、Step 4 做一个 Webview 面板
    • 本地资源该怎么引
    • Webview 和插件怎么通信
  • 五、Step 5 打包、发布和升级
    • 认识 vsce
    • 申请发布权限
    • 升级已发布的插件
    • 本地安装 vsix
  • 六、发布前的 checklist
  • 总结
  • 参考

← webpack plugin原理分析与实践 手写四个实用插件小程序绘制海报总结 canvas 两种画法与踩坑 →