乐趣区

关于vscode插件:VSCode-For-Web-深入浅出-插件加载机制

最近我在浏览 VSCode for web 的 repo,在最近更新的一些 commit 中发现了一个新的 VSCode 插件个性反对,名为 webOpener,它的作用是什么呢?又是如何影响插件加载的呢?在这一篇中咱们联合 VSCode For Web 的插件加载机制来详细分析一下。

VSCode for web 的插件加载机制

咱们晓得,因为 VSCode for web 运行在浏览器上,因而,它的插件加载机制与 VSCode for desktop 有所不同。

在 VSCode for desktop 中,插件是以 vsix 包的模式存在的,因而,VSCode for desktop 能够间接通过 vsix 包的模式加载插件。而在 VSCode for web 中,因为浏览器的平安机制,不能间接加载 vsix 包。

因而,VSCode for web 采纳了一种非凡的插件加载机制。公布 VSCode for web 插件时,公布零碎会间接将我的项目编译,并公布到 CDN 节点上。当用户加载插件时,通过向该指标 url 发送申请,拉取远端(也能够是本地)的 extension.js 文件。并利用 web worker 加载机制,为每个插件调配独立线程加载与执行。

在生产环境中,对每个进入 VSCode 插件商店的插件,VSCode for web 会将反对 web 环境的插件的 package.jsonextension.js 等文件打包成一个 zip 包,而后依据 publisher 调配适合的二级域名,通过 CDN 散发。

以我在应用的 One Dark Pro 主题为例:

而在调试模式中,咱们能够通过 Install extension from location...命令,指定编译后插件的 url,从而加载插件。

咱们应用本地服务器,指定一个已编译好的 VSCode web extension,并填入本地服务器地址 (https://localhost:5000),并刷新页面,那么从 Chrome 的 Network 中能够看到 VSCode 向指标地位申请了package.jsonextension.js,并看到插件曾经被胜利加载了。

通过这样的形式,VSCode for web 在每次页面关上后,实现了对用户自定义的插件治理与加载。并因为 web worker 的个性,每个插件的执行环境都是独立且互相隔离的。

通过非凡 url 路由的形式的插件加载机制

VSCode for web 最突出的特点是它是运行在浏览器上的,因而,咱们能够利用 url,来实现一些微妙的新个性。例如,通过非凡的 url 路由,免装置地加载插件。

目前,vscode.dev 能够应用这样的形式加载插件:

https://vscode.dev/+publisher.name

例如,在浏览器中输出 https://vscode.dev/+ms-vscode.onedrive-browser 将加载 OneDrive 浏览器扩大。

当然,咱们也能够应用同样的形式加载本地编译的插件。因为 vscode.dev 强制要求 secure context,因而,咱们须要在本地启动一个 https 的服务器,并对 url 进行 base64 编码,能力失常拜访。

拜访 https://vscode.dev/+aHR0cHM6Ly9sb2NhbGhvc3Q6MzAwMA== 即可。(前面那一段为 ”https://localhost:3000″) 的 base64 编码)

webOpener 个性介绍

有开发过 VSCode for desktop 的插件的同学应该晓得,vscode 插件的所有能力都是在 package.json 中申明的,这也是为什么 VSCode 除了须要加载入口的 extension.js 外,还肯定要加载插件的 package.json 的起因。

在插件 package.jsoncontributes 字段中,咱们能够申明插件的各种能力,例如,命令、菜单、快捷键、主题、语言、调试器等等。

对于 vscode for web 版本的插件来说,咱们还能够申明 webOpener 能力,其所有属性都是可选的。申明如下:

{
  "name": "onedrive-browser",
  "contributes": {
    "webOpener": {
      "scheme": "onedrive",
      "import": "webOpener.js",
      "runCommands": [{"command": "hello-world", "args": ["$url"] }]
    }
    ...
  }
}

webOpener.scheme

默认状况下,vscode.dev/+publisher.name 路由将间接关上默认的 VSCode 示例工作区。然而,如果提供了 scheme path,则 VSCode 将依据路由参数关上一个以该协定关上 url 中后续 path 指向的文件夹,格局如下:

# 当 scheme 设置为 onedrive
https://vscode.dev/+publisher.name/remoteAuthority/path/segments/...

例如,当插件 webOpener 的 scheme 设置为 onedrive 时,拜访 https://vscode.dev/+ms-vscode.onedrive-browser/myPersonalDrive/cool/folder,此时拜访 url 将重定向为 onedrive:///myPersonalDrive/cool/folder

若此协定不在 VSCode 的内置协定中,咱们能够在插件中通过 vscode.workspace.registerFileSystemProvider 这个 API 注册自定义的 FileSystemProvider,从而实现对自定义协定的 FileSystem 反对。

实质上,它关上的形式与 VSCode for web 的 vscode.open 命令也是统一的。

webOpener.runCommands

当 VSCode 的主 workbench 加载结束后,会触发 webOpeneronDidCreateWorkbench 的钩子,并执行此处申明的命令集。

这将传入一个命令数组,例如:[{"command": "test-extension.hello-world", "args": ["$url"] }],此时将能够执行自定义插件 test-extension 的相干命令。

其中,$url 指代以后页面 url。如果插件的初始化依赖来自 url 的 query/path 等等信息,这将很有用。

webOpener.import

这里定义了 webOpener 加载的入口点。它是一个绝对于插件 package.json 的 ES Module 门路,例如:webOpener.js

它与 extension.js 一样,默认导出一个 doRoute 函数,该函数将获取 route 与 workbench 等信息(workbench 这个实例中提供了以后 vscode for web 的命令、日志、环境、window、workspace 等多种能力反对)。因为 webOpener.js 运行在主线程中,因而它能做到的事件要比处于 web worker 下的 vscode for web 插件更多。

举一个例子,这是一个简略的 webOpener 奉献 onedrive-browser:

export default function doRoute(route) {
  // If we're not already opening a OneDrive, show the picker immediately
  // when the user hits `vscode.dev/+ms-vscode.onedrive-browser`.
  if (route.workspace.folderUri?.scheme !== 'onedrive') {
    route.onDidCreateWorkbench.runCommands.push({
      command: 'onedrive-browser.openOneDrive',
      args: [],});
  }
}

它将在 workbench 加载结束后,判断以后的 workspace 是否为 onedrive,如果不是,则执行 onedrive-browser.openOneDrive 命令,从而关上 onedrive 文件夹。

webOpener 与插件的通信机制

在理解了 webOpener 的根本个性之后,咱们来看看该如何利用这些个性,与咱们的 web 插件进行通信,从而扩大插件能力。

咱们能够看出,因为 webOpener 加载在主线程,且 doRoute 办法的执行机会在主线程 workbench 加载结束之后,在申请远端插件并执行之前。因而,咱们能够有两种形式来传递信息,与处于 web worker 下,与宿主隔离的插件进行通信。

第一种即为在 runCommands 中介绍的,通过执行 command 并传递 url 的形式传递初始化信息。该形式也是 webOpener 与插件通信的罕用形式之一,用于为初始化插件时提供局部依赖参数。

第二种则是通过 doRoute 办法,捕捉此时的申请信息,并依据申请信息的不同对插件能力进行不同的变更,但实质上还是通过 command 的形式给插件发送 args 来实现的。

我在以后最新版本的 vscode-dev 代码库中(1.79.0),并未发现间接通过 webOpener 裸露相似 postMessage 的与插件通信的办法,因而到目前为止,咱们只能通过给插件的 command 形式触发 trigger 与传入参数这一种形式来实现与插件的通信。这导致了在 web 下插件的能力其实相当受限。

总结

本篇文章解析了在 VSCode for web 中的插件加载机制,以及如何通过 webOpener 个性来扩大插件的能力。

咱们能够看出,在现阶段的 VSCode for web 中,插件的加载机制也仅仅只是做到了可用状态。因为 web worker 人造的与主线程隔离的个性,desktop 的很多好用的功能性插件(即除了 theme/key-binding 这种不须要执行逻辑的插件之外)在 web 端的反对还是会遇到很多问题,并不能无缝迁徙。这点也是我在尝试开发 VSCode for web 插件时最大的痛点。

不过,随着 VSCode for web 我的项目仍在进行高频的开发与欠缺,心愿将来的 VSCode for web 能在插件开发与应用上尽可能对齐甚至兼容 desktop 的体验。

参考资料

  • VSCode dev Repository(目前是公有仓库,须要向 Microsoft 申请权限,将来功能完善后或将凋谢)
  • VSCode for the Web Introduction
退出移动版