193

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

 4 years ago
source link: https://blog.dteam.top/posts/2020-03/simplify-vscode-webview-development-with-vscode-page.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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

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

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

  • 负责 GUI 部分的 HTML 页面
  • 充当 vscode 和 HTML 页面之间桥梁的 vscode.WebviewPanel

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

  • 设置 html 的 resource root 。
  • createOrShow 逻辑:即有则显示,无则创建。
  • 隐藏时保存之前页面的状态。
  • html 与 WebviewPanel 之间交互涉及的 boilerplate code:开发者只需考虑两者之间的消息格式和处理逻辑就好了。

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

于是, vscode-page 应运而生。

设计原理

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

mAVZFzr.png!web

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

  • vscode-page 充当消息 dispatcher 。
  • MessageMapping 定义消息、处理器和页面模板(采用流行的 handlebars.js )这三者的关系。
  • Response 的 html 片段用于更新指定 element 的 innerHTML。

更详细的说明请参见 README

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

使用指南

vscode-page 的使用很简单:

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

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

  • 请求:command + parameters
  • 响应:command + ( contents 或 result )

编写页面

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

<base href="{{base}}" />
<script type="text/javascript">
  "{{init}}"
</script>
  • 前者用于初始化资源根目录,这样后面的资源引用可以直接引用相对路径即可。
  • 后者会注入一个 EventListerner 函数定义。

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

  • 若消息的 command 为 Response 结尾且有 contents 属性,则为消息中指定的 element 替换 innerHTML 。
  • 对于不满足上面条件的消息,则丢弃。

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

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

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

定义 MessageMapping 。

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

  • command,与请求中的 command 对应
  • handler,处理逻辑
  • templates,请求完毕后要显示的模板定义

详细例子,参见: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 ,因此用到的插件和规范如下:

代码风格:

风格可商量余地:

  • “strict”: true + “no-any”: false,允许

相关插件:

  • ESLint
  • Bracket Pair Colorizer 2
  • Prettier
  • markdownlint
  • indent-rainbow
  • Path Intellisense
  • Peacock

写在最后

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK