DTeam 团队日志

Doer、Delivery、Dream

使用 vscode-page 简化 vscode 插件的 Webview 开发

胡键 Posted at — Mar 12, 2020 阅读

vscode 是时下最流行的开发工具之一,高逼格的 UI 和各种酷酷的插件,不仅提高了使用者的开发效率,还让他们的形象大为改善,不再是人们眼中的“死肥宅”。使用 vscode 很潮,但更潮的就是为它开发插件。作为今年的兴趣点,我也投身于 vscode 插件开发者大军之中。

vscode 的插件开发并不需要太高深的技术,就是普通的开发技术的组合。因为面向的用户就是开发者,所以一般情况下主要是对开发者工作现场(如编辑文本框)的增强,并搭配完成任务的各种命令,很少需要复杂的 UI 。但凡事总有例外,当需要 GUI 加持的时候,就轮到 Webview 出场了。

Webview 开发本身也很简单,就两个任务:

可是,就算是简单的任务,做多了你也会发现还是存在一些值得优化的空间,避免在每个新页面开发都重复以下的事情:

这样做有助于设定一个简单的开发模型,处理好消息、处理和展示三者的关系,不必每次开发新页面时“copy - paste - change”,而是采用一致的模型并促进组内协作。

于是,vscode-page 应运而生。

设计原理

简单地讲,vscode-page 是一个针对 vscode Webview 开发的轻量级页面微框架,抽象了 html 与 WebviewPanel 之间的通信交互,使得开发者只需要关心具体业务逻辑。其整个架构图如下:

vscode-page

看到这张图,熟悉 Web 开发框架的同学应该已经秒懂整个设计:

更详细的说明请参见 README

这里没有采用花哨的 Angular 、React 和 Vue ,主要就是为了简单和轻量。并且,在 vscode Webview 这种受限的环境下,使用传统的 jQuery + BootStrap + Handlebars 就是最佳选择,而且一般情况下插件的 GUI 也不会太复杂,前面的几个工具已经足以应对。

使用指南

vscode-page 的使用很简单:

定义页面和 WebviewPanel 之间的消息交互。

在这里定义消息请求和响应内容,其中:

编写页面

这里有几点注意,在每个页面前面添加以下两行:

<base href="{{base}}" />
<script type="text/javascript">
  "{{init}}"
</script>

一般情况下,你直接调用:initEventListener() 来完成初始化就行了,它实现了:

假如你想处理,则可以换一种初始化方式,传入处理函数:

initEventListener(message => {...});

此时,你可以从 message.result 拿到结果。

定义 MessageMapping 。

这个过程没有什么复杂的,就是定义三个属性:

详细例子,参见:https://github.com/DTeam-Top/vscode-page/blob/master/example/src/home.ts 。

值得一提的是,MessageMapping 还支持 forward 模式,即请求处理之后,直接转到另一个 command ,以它的处理结果为最终处理结果,类似 Web 开发中的 forward 模式。

  {
    command: "submitRepositories",
    handler: async parameters => {
      ……
    },
    forward: "ready"
  },

上面的 ready 就是另一个 MessageMapping 定义的 command 名。

创建 WebviewPannel

这个最简单,就一句话:

createOrShowPage(
  'name',
  'ext.home',
  'Sample Page',
  'pages',
  'home.html',
  context,
  messageMappings
);

定义了 title、根目录和需要加载的页面。

关于使用的完整例子请结合 README样例工程来看,后者是我们即将试水发布的一个 vscode 插件,用于帮助开发者或企业搭建自己的私有插件仓库。因为总有些时候会需要私有插件仓库,:)

关于 vscode 插件开发

资源根目录

虽然插件开发指南已经很详尽,但初学者总是会遗漏一些细节,在开发时才发现文档中早有提及。这里尤其值得一提的就是 Webview 的资源加载问题,因为它浪费了我一些时间。

最简单的方案,当然就是直接使用 vscode-page 插件就行了,它已经替你 cover 了一些细节,你只需照常使用就行,你可以查看这个页面

假如不使用 vscode-page ,我也没打算藏着掖着,只需要复制下面的几行代码即可:

const rootString = path.join(context.extensionPath, base);
const localResourceRoots = vscode.Uri.file(path.join(rootString, '/')).with(
  {
    scheme: 'vscode-resource',
  }
);
panel = vscode.window.createWebviewPanel(
  viewType,
  title,
  vscode.ViewColumn.One,
  {
    enableScripts: true,
    retainContextWhenHidden: true,
    localResourceRoots: [localResourceRoots],
  }
);
const pagePath = path.join(rootString, page);
panel.webview.html = fs
  .readFileSync(pagePath, 'utf-8')
  .replace('{{base}}', localResourceRoots.toString());

当然,你得记得在页面中在添加一行:

<base href="{{base}}" />

从代码中我相信你已经看出来一些端倪:scheme: ‘vscode-resource’,它并非一般 Web 开发中的 file: 。同时也要记得设置 localResourceRoots 属性。在开发 vscode-page 时发现使用 vscode-page 的插件总是没法加载自己的资源,报:ERR_ACCESS_DENIED ,后来发现是因为没有设置这个属性。

调试工具

开发 Webview 肯定需要类似 Web Dev Tools 那样的浏览器查看工具,vscode 也有。输入:Open Webview Developer Tools 就能激活。

工程相关

因为本质上 vscode 插件开发是前端工程的范畴,并且我们团队主要采用 TypeScript ,因此用到的插件和规范如下:

代码风格:

风格可商量余地:

相关插件:

写在最后

作为插件发布的前奏,vscode-page 已经发布(0.0.1),工程路径:https://github.com/DTeam-Top/vscode-page ,欢迎加星或使用,😄。