支持远程开发和 GitHub Codespaces
Visual Studio Code 远程开发 使您能够透明地与位于其他机器(无论是虚拟机还是物理机)上的源代码和运行时环境进行交互。GitHub Codespaces 是一种服务,通过管理和托管的云托管环境扩展了这些功能,这些环境可以从 VS Code 和基于浏览器的编辑器中访问。
为了确保性能,Remote Development 和 GitHub Codespaces 都会透明地远程运行某些 VS Code 扩展。然而,这可能对扩展的运作方式产生微妙的影响。虽然许多扩展在不进行任何修改的情况下可以正常工作,但您可能需要进行更改,以确保扩展在所有环境中都能正常运行,尽管这些更改通常相当轻微。
本文总结了远程开发和代码空间(包括扩展)的架构,以及如何在远程工作区或代码空间中调试您的扩展,还有关于如果扩展不能正常工作的建议。
建筑和扩展种类
为了使用户在使用远程开发或Codespaces时尽可能透明,VS Code将扩展分为两种类型:
-
用户界面扩展:这些扩展贡献于 VS Code 用户界面,并且始终在用户的本地机器上运行。用户界面扩展无法直接访问远程工作区中的文件,或运行该工作区或机器上安装的脚本/工具。示例用户界面扩展包括:主题、片段、语言语法和键盘映射。
-
工作区扩展:这些扩展在与工作区相同的机器上运行。当在本地工作区中时,工作区扩展在本地机器上运行。当在远程工作区或使用Codespaces时,工作区扩展在远程机器/环境中运行。工作区扩展可以访问工作区中的文件,以提供丰富的、多文件的语言服务、调试器支持,或对工作区中的多个文件执行复杂的操作(直接或通过调用脚本/工具)。虽然工作区扩展主要不专注于修改UI,但它们可以贡献资源管理器、视图和其他UI元素。
当用户安装一个扩展时,VS Code 会根据其类型自动将其安装到正确的位置。如果一个扩展可以以两种类型运行,VS Code 会尝试为这种情况选择最佳类型;UI 扩展将在 VS Code 的 本地扩展主机 中运行,而工作区扩展将在一个 远程扩展主机 中运行,该主机位于一个小型 VS Code 服务器如果在远程工作区中存在,则使用它,否则如果在本地存在,则在 VS Code 的本地扩展主机上运行。为了确保最新的 VS Code 客户端功能可用,服务器需要与 VS Code 客户端版本完全匹配。因此,当您在容器中、在远程 SSH 主机上、使用 Codespaces 或在 Windows 子系统 for Linux (WSL) 中打开文件夹时,服务器会由 Remote Development 或 GitHub Codespaces 扩展自动安装(或更新)。(VS Code 也会自动管理启动和停止服务器,因此用户不会意识到它的存在。)

VS Code 的 API 设计为在从用户界面 (UI) 或工作区扩展调用时自动在正确的机器(本地或远程)上运行。然而,如果您的扩展使用 VS Code 不提供的 API(例如使用 Node API 或运行 shell 脚本),在远程运行时可能无法正常工作。我们建议您测试您的扩展的所有功能在本地和远程工作区中是否都能正常工作。
调试扩展
在你可以将扩展的开发版本安装在远程环境中进行测试,如果你遇到问题,你可能希望直接在远程环境中调试你的扩展。在本节中,我们将介绍如何在GitHub Codespaces、本地容器、SSH主机或WSL中编辑、启动和调试你的扩展。
通常情况下,您测试的最佳起点是使用限制端口访问的远程环境(例如 Codespaces、容器或具有限制性防火墙的远程 SSH 主机),因为这些环境中运行的扩展通常也能在像 WSL 这样的限制较少的环境中运行。
使用 GitHub Codespaces 进行调试
在GitHub Codespaces预览版中调试您的扩展是一个很好的起点,因为您可以使用VS Code和Codespaces基于浏览器的编辑器进行测试和故障排除。您也可以根据需要使用自定义开发容器。
请按照以下步骤操作:
-
导航到包含您的扩展的 GitHub 仓库并在 codespace 中打开它,以便在基于浏览器的编辑器中与其一起工作。您也可以在 VS Code 中打开 codespace,如果您更喜欢。
-
虽然 GitHub Codespaces 的默认图像应该为大多数扩展提供所有必要的前提条件,但您可以安装任何其他所需的依赖项(例如,使用
yarn 安装或请将以下网页文本翻译成中文,只输出翻译结果,不输出任何其他解释,若文本已经是中文了,则直接用中文复述一遍。翻译结果保持格式不变。输入:sudo apt-get) 在新的 VS Code 终端Windows中 (⌃⇧` (Windows, Linux Ctrl+Shift+`)). -
最后,按F5或使用运行和调试视图在代码空间内启动扩展。
注意: 您将无法在出现的Windows中打开扩展源代码文件夹,但您可以在代码空间中打开子文件夹或代码的其他地方。
出现的扩展开发主机Windows将包括您的扩展在带有附加调试器的 codespace 中运行。
在自定义开发容器中调试
请按照以下步骤操作:
-
要本地使用开发容器,安装并配置Dev Containers扩展,然后使用文件 > 打开... / 打开文件夹...在VS Code中本地打开您的源代码。要使用Codespaces,请导航到包含您的扩展的GitHub仓库并在Codespaces中打开它以在基于浏览器的编辑器中进行编辑。您也可以在VS Code中打开Codespaces(如果您更喜欢)。
-
选择 开发容器:添加开发容器配置文件... 或 代码空间:添加开发容器配置文件... 从命令面板 (F1),然后选择 Node.js & TypeScript (或 Node.js 如果您不使用 TypeScript)以添加所需的容器配置文件。
-
可选: 运行此命令后,您可以修改
.devcontainer文件夹以包含额外的构建或运行时要求。请参阅详细的 创建开发容器 文档了解更多信息。 -
运行 开发容器:在容器中重新打开 或 代码空间:添加开发容器配置文件... 不久之后,VS Code 将会设置容器并连接。您现在将能够像在本地情况下一样在容器内开发您的源代码。
-
执行
yarn 安装或npm 安装在新的 VS Code 终端Windows中 (⌃⇧` (Windows, Linux Ctrl+Shift+`)),以确保安装 Linux 版本的 Node.js 原生依赖项。您也可以安装其他操作系统或运行时依赖项,但您可能希望将这些添加到.devcontainer/Dockerfile这样,如果重建容器,它们也是可用的。 -
最后,按F5或使用运行和调试视图来启动此容器内的扩展并附加调试器。
注意: 您将无法在出现的Windows中打开扩展源代码文件夹,但您可以打开容器中的子文件夹或其他位置。
出现的扩展开发主机Windows将包括在第2步中您定义的容器中运行的扩展,并且调试器已附加到它上面。
使用 SSH 进行调试
请按照以下步骤操作:
-
After 安装并配置远程 - SSH 扩展,从 VS Code 的命令面板 (F1) 中选择 Remote-SSH: 连接到主机... 来连接到一个主机。
-
一旦连接,可以使用文件 > 打开... / 打开文件夹...选择包含您的扩展源代码的远程文件夹,或者选择Git: 克隆从命令面板 (F1) 克隆并打开在远程主机上。
-
安装任何可能缺失的依赖项(例如使用
yarn 安装或apt-get) 在新的 VS Code 终端Windows中 (⌃⇧` (Windows, Linux Ctrl+Shift+`)). -
最后,按F5或使用运行和调试视图来启动在远程主机上的扩展并附加调试器。
注意: 您将无法在出现的Windows中打开扩展源代码文件夹,但您可以打开子文件夹或在 SSH 主机的其他地方打开。
出现的扩展开发主机Windows将包括在附加了调试器的 SSH 主机上运行的扩展。
使用WSL进行调试
请按照以下步骤操作:
-
在安装并配置WSL扩展之后,从VS Code的命令面板(F1)中选择WSL: 新Windows。
-
在出现的新Windows中,使用文件 > 打开... / 打开文件夹...选择包含您的扩展源代码的远程文件夹,或者选择Git: 克隆从命令面板(F1)克隆并打开它在WSL中。
提示: 您可以选择
/mnt/c文件夹以访问您在Windows侧克隆的任何源代码。 -
安装任何可能缺失的依赖项(例如使用
apt-get) 在新的 VS Code 终端Windows中 (⌃⇧` (Windows, Linux Ctrl+Shift+`))。你至少需要运行yarn 安装或npm 安装确保本地Node.js依赖的Linux版本可用。 -
最后,按F5或使用运行和调试视图来启动扩展并附加调试器,就像在本地一样。
注意: 您将无法在出现的Windows中打开扩展源代码文件夹,但您可以在WSL中打开子文件夹或其他地方。
出现的扩展开发主机Windows将包括在附加了调试器的WSL中运行的你的扩展。
安装您的扩展的开发版本
任何时间 VS Code 自动在 SSH 主机、容器或 WSL 内,或通过 GitHub Codespaces 安装扩展时,使用的是 MarketPlace 版本(而不是你本地已经安装的版本)。
虽然这在大多数情况下都是合理的,但您可能希望在不需要设置调试环境的情况下使用(或共享)未发布的扩展版本进行测试。要安装未发布的扩展版本,您可以将扩展打包为一个VSIX并手动将其安装到已经连接到运行中的远程环境的 VS Code Windows中。
请按照以下步骤操作:
- 如果这是一个已发布的扩展,您可能需要添加
"extensions.autoUpdate": false到settings.json以防止它自动更新到最新的Marketplace版本。 - 接下来,使用
vsce 包将你的扩展打包成VSIX。 - 连接到一个代码空间、开发容器、SSH主机或WSL环境。
- 使用 从 VSIX 安装... 命令在扩展视图中 更多操作 (
输入:...) 菜单以在此特定Windows(不是本地Windows)中安装扩展。 - 在提示时重新加载。
提示: 安装后,您可以使用开发者:显示正在运行的扩展命令查看 VS Code 是在本地还是远程运行扩展。
处理远程扩展的依赖关系
扩展可以依赖其他扩展的API。例如:
- 一个扩展可以从他们的
激活功能。 - 此 API 将向在相同扩展主机中运行的所有扩展提供。
- 消费者扩展声明在他们的
package.json他们依赖于使用提供的扩展。扩展依赖财产。
当所有扩展都在本地运行并且共享相同的扩展主机时,扩展依赖项可以正常工作。
在处理远程场景时,可能远程运行的扩展对本地运行的扩展有依赖。例如,本地扩展暴露一个对远程扩展功能至关重要的命令。在这种情况下,我们建议远程扩展将本地扩展声明为扩展依赖但是问题在于,这些扩展运行在两个不同的扩展主机上,这意味着提供者的 API 对消费者不可用。因此,要求提供扩展完全放弃通过使用来导出任何 API 的能力。"api": "无"在他们的扩展中package.json这些扩展仍然可以使用 VS Code 命令进行通信(这些命令是异步的)。
这可能看起来是对提供扩展的一个不必要的严格限制,但一个使用扩展的"api": "无"只放弃了从其返回API的能力激活方法。消费者扩展在其他扩展主机上执行时,仍然可以依赖它们并且将被激活。
常见问题
VS Code 的 API 被设计为自动在正确的位置运行,无论你的扩展位于何处。考虑到这一点,有一些 API 可以帮助你避免意外行为。
执行位置不正确
如果您的扩展没有按预期工作,它可能运行在错误的位置。最常见的情况是,当您期望它仅在本地运行时,扩展却远程运行。您可以使用开发者:显示正在运行的扩展命令从命令面板(F1)查看扩展的运行位置。
如果开发者:显示正在运行的扩展命令显示一个UI扩展被错误地视为工作区扩展,或者反之,尝试设置扩展类型 在你的扩展的 package.json 中的属性,如 扩展类型部分所述。
你可以快速测试更改扩展类型的效果,使用远程.扩展类型 设置。此设置是一个将扩展 ID 映射到扩展类型的映射。例如,如果您想强制Azure 数据库扩展成为 UI 扩展(而不是其默认的 Workspace)以及远程 - SSH:编辑配置文件扩展成为工作区扩展(而不是其默认的 UI),则应设置:
{
"远程扩展类型": {
"ms-azuretools.vscode-cosmosdb": ["ui",
"ms-vscode-remote.remote-ssh-edit": ["工作区"]
}
}
使用远程.扩展类型允许您快速测试已发布的扩展版本,而无需修改它们package.json并重建它们。
持久化扩展数据或状态
在某些情况下,您的扩展可能需要持久化不属于settings.json或单独的工作区配置文件(例如.eslintrc). 为了解决这个问题,VS Code 在 vscode.扩展上下文在激活期间传递给你的扩展的对象。如果你的扩展已经利用了这些属性,无论在何处运行,它都应该继续正常工作。
然而,如果您的扩展依赖于当前 VS Code 路径规范(例如~/.vscode) 或者某些操作系统文件夹的存在 (例如~/.config/Code在 Linux 上保存数据可能会遇到问题。幸运的是,更新你的扩展以避免这些挑战应该是简单的。
如果你正在使用简单的键值对,你可以使用来存储工作区特定或全局状态信息vscode.ExtensionContext.workspaceState或vscode.ExtensionContext.globalState分别。如果您的数据比键值对更复杂, 全局存储路径和存储路径属性提供“安全”的URI,您可以使用它们来读取/写入文件中全局工作区特定的信息。
要使用API:
导入 * 作为 vscode 来自 'vscode';
export function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(
vscode.commands.registerCommand('myAmazingExtension.persistWorkspaceData', async () => {
if (!context.storageUri) {
return;
}
// Create the extension's workspace storage folder if it doesn't already exist
try {
// When folder doesn't exist, and error gets thrown
await vscode.workspace.fs.stat(context.storageUri);
} catch {
// Create the extension's workspace storage folder
await vscode.workspace.fs.createDirectory(context.storageUri)
}
const workspaceData = vscode.Uri.joinPath(context.storageUri, 'workspace-data.json');
const writeData = new TextEncoder().encode(JSON.stringify({ now: Date.now() }));
vscode.workspace.fs.writeFile(workspaceData, writeData);
}
));
context.subscriptions.push(
vscode.commands.registerCommand('myAmazingExtension.persistGlobalData', async () => {
if (!context.globalStorageUri) {
return;
}
// Create the extension's global (cross-workspace) folder if it doesn't already exist
try {
// When folder doesn't exist, and error gets thrown
await vscode.workspace.fs.stat(context.globalStorageUri);
} catch {
await vscode.workspace.fs.createDirectory(context.globalStorageUri)
}
```plaintext
const workspaceData = vscode(Uri.joinPath(context.globalStorageUri, 'global-data.json'));
const writeData = new TextEncoder().encode(JSON.stringify({ now: Date.now() }));
vscode.workspace.fs.writeFile(workspaceData, writeData);
```
在不同机器之间同步用户全局状态
如果您的扩展需要在不同机器之间保存一些用户状态,请将状态提供给设置同步 使用vscode.ExtensionContext.globalState.setKeysForSync这可以帮助防止在多台机器上向用户显示相同的欢迎或更新页面。
有一个使用示例设置同步密钥 在 扩展功能 主题中。
持久化秘密
如果你的扩展需要持久化密码或其他秘密,你可能想使用Visual Studio Code的SecretStorage API,它提供了一种通过加密在文件系统上安全存储文本的方法。例如,在桌面端,我们使用Electron的safeStorage API来在将秘密存储到文件系统之前进行加密。该API总是将秘密存储在客户端,但无论你的扩展在何处运行,你都可以使用此API检索相同的秘密值。
注意:此 API 是持久化密码和秘密的推荐方法。您应不使用
vscode.ExtensionContext.workspaceState或vscode.ExtensionContext.globalState因为这些API以明文形式存储数据。
这是一个例子:
import * as vscode from 'vscode';
导出 函数 激活(上下文: vscode.扩展上下文) {
// ...
const myApiKey = 上下文.秘密.获取('apiKey');
// ...
上下文.秘密.删除('apiKey');
// ...
上下文.秘密.存储('apiKey', myApiKey);
}
使用剪贴板
历史上,扩展作者使用了Node.js模块,例如剪贴板与剪贴板进行交互。不幸的是,如果您在工作区扩展中使用这些模块,它们将使用远程剪贴板而不是用户的本地剪贴板。VS Code 剪贴板 API 解决了这个问题。它始终在本地运行,无论调用它的扩展类型如何。
要在一个扩展中使用 VS Code 剪贴板 API:
导入 * 作为 vscode 来自 'vscode';
export function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(
vscode.commands.registerCommand('myAmazingExtension.clipboardIt', async () => {
// Read from clipboard
const text = await vscode.env.clipboard.readText();
// 写入剪贴板
await vscode.env.clipboard.writeText(
`您似乎正在复制 "${text}'。您需要帮助吗?`
);
})
);
}
在本地浏览器或应用程序中打开某些内容
启动一个进程或使用一个模块像打开 启动浏览器或其他应用程序以特定的URI可以很好地适用于本地场景,但是Workspace Extensions远程运行,这可能导致应用程序在错误的一侧启动。VS Code远程开发 部分地 模拟了 打开节点模块允许现有扩展正常运行。你可以通过URI调用该模块,VS Code将在客户端显示该URI的默认应用程序。然而,这并不是一个完整的实现,因为不支持选项,并且一个子进程对象未被返回。
与其依赖第三方节点模块,我们建议扩展利用vscode.env.openExternal方法可以启动本地操作系统上注册的默认应用程序来处理给定的URI。更棒的是,vscode.env.openExternal 自动本地主机端口转发! 您可以使用它来指向远程机器或代码空间上的本地Web服务器,即使该端口在外部被阻止,也能提供内容。
注意: 目前,Codespaces 浏览器基于的编辑器中的转发机制仅支持 http 和 https 请求。然而,当从 VS Code 连接到代码空间时,您可以与任何 TCP 连接进行交互。
使用vscode.env.openExternal应用程序编程接口:
导入 * 作为 vscode 来自 'vscode';
export async function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(
vscode.commands.registerCommand('myAmazingExtension.openExternal', () => {
// Example 1 - Open the VS Code homepage in the default browser.
vscode.env.openExternal(vscode.Uri.parse('https://code.visualstudio.com'));
// 示例 2 - 打开一个自动转发的本地 HTTP 服务器。
vscode.env.openExternal(vscode.Uri.parse('http://localhost:3000'));
// 示例 3 - 打开默认的电子邮件应用程序。
vscode.env.openExternal(vscode.Uri.parse('mailto:<填入您的电子邮件>'));
})
);
}
转发本地主机
虽然 在 vscode.env.openExternal 有用,也可能有这样一种情况,您希望转发某些内容,而不真正启动新的浏览器Windows或应用程序。这就是vscode.env.asExternalUriAPI 出场。
注意: 目前,Codespaces 浏览器基于的编辑器中的转发机制仅支持 http 和 https 请求。然而,当从 VS Code 连接到代码空间时,您可以与任何 TCP 连接进行交互。
使用vscode.env.asExternalUri应用程序编程接口:
导入 * 作为 vscode 从 'vscode';
导入 { getExpressServerPort } 从 './server';
export async function activate(context: vscode.ExtensionContext) {
const dynamicServerPort = await getWebServerPort();
context.subscriptions.push(vscode.commands.registerCommand('myAmazingExtension.forwardLocalhost', async () =>
// 使端口在本地可用并获取完整的URI
const fullUri = await vscode.env.asExternalUri(
vscode.Uri.parse(`http://localhost:${dynamicServerPort}`));
// ... 对 fullUri 做一些事情 ...
}));
}
需要注意的是,API 返回的 URI可能根本不参考 localhost,因此您应该完全使用它。这对于基于浏览器的 Codespaces 编辑器尤为重要,因为不能使用 localhost。
回调和URI处理程序
该vscode.window.registerUriHandler API 允许您的扩展注册一个自定义的 URI,如果在浏览器中打开,将在您的扩展中触发一个回调函数。注册 URI 处理程序的常见用例是在实现服务登录时使用 OAuth 2.0 认证提供者(例如,Azure AD)。然而,它可以用在任何您希望外部应用程序或浏览器向您的扩展发送信息的场景。
VS Code 中的远程开发和Codespaces扩展将透明地处理将URI传递给您的扩展,无论它实际运行在本地还是远程。然而,vscode://URIs 无法与 Codespaces 基于浏览器的编辑器一起使用,因为像浏览器这样的程序打开这些 URIs 时,会尝试将它们传递给本地 VS Code 客户端,而不是基于浏览器的编辑器。幸运的是,可以通过使用 来轻松解决这个问题vscode.env.asExternalUri应用程序编程接口。
让我们使用组合vscode.window.registerUriHandler和vscode.env.asExternalUri要设置一个示例OAuth认证回调:
导入 * 作为 vscode 来自 'vscode';
// 这是 ${publisher}.${name} 来自 package.json
const extensionId = 'my.amazing-extension';
export async function activate(context: vscode.ExtensionContext) {
// Register a URI handler for the authentication callback
vscode.window.registerUriHandler({
handleUri(uri: vscode.Uri): vscode.ProviderResult<void> {
// Add your code for what to do when the authentication completes here.
if (uri.path === '/auth-complete') {
vscode.window.showInformationMessage('Sign in successful!');
}
}
});
// Register a sign in command
context.subscriptions.push(
vscode.commands.registerCommand(`${extensionId}.signin`, async () => {
// Get an externally addressable callback URI for the handler that the authentication provider can use
const callbackUri = await vscode.env.asExternalUri(
vscode.Uri.parse(`${vscode.env.uriScheme}://${extensionId}/auth-complete`)
);
// Add your code to integrate with an authentication provider here - we'll fake it.
vscode.env.clipboard.writeText(callbackUri.toString());
await vscode.window.showInformationMessage(
'Open the URI copied to the clipboard in a browser window to authorize.'
);
})
);
}
在 VS Code 中运行此示例时,它会连接一个vscode://或vscode-insiders://可以作为身份验证提供程序回调的URI。在Codespaces基于浏览器的编辑器中运行时,它会连接一个https://*.github.devURI没有代码更改或特殊条件。
虽然OAuth超出了本文件的范围,但请注意,如果您将此示例改编为实际的认证提供商,您可能需要在提供商前面构建一个代理服务。这是因为并非所有提供商都允许vscode:// 回调 URIs 和其他回调不支持通过 HTTPS 进行的回调使用通配符主机名。我们还建议在可能的情况下使用 OAuth 2.0 授权码与 PKCE 流程(例如,Azure AD 支持 PKCE)以提高回调的安全性。
远程运行或在Codespaces浏览器编辑器中运行时行为有所不同
在某些情况下,您的Workspace Extension可能需要在远程运行时更改行为。在其他情况下,您可能希望在Codespaces基于浏览器的编辑器中更改其行为。VS Code提供了三个API来检测这些情况:vscode.env.uiKind,扩展.扩展类型,和vscode.env.远程名称输入:.
接下来,您可以按如下方式使用这三个API:
导入 * 作为 vscode 来自 'vscode';
export async function activate(context: vscode.ExtensionContext) {
// extensionKind returns ExtensionKind.UI when running locally, so use this to detect remote
const extension = vscode.extensions.getExtension('your.extensionId');
if (extension.extensionKind === vscode.ExtensionKind.Workspace) {
vscode.window.showInformationMessage('I am running remotely!');
}
// Codespaces browser-based editor will return UIKind.Web for uiKind
if (vscode.env.uiKind === vscode.UIKind.Web) {
vscode.window.showInformationMessage('I am running in the Codespaces browser editor!');
}
// 如果在本地工作区中工作,VS Code 将返回未定义的 remoteName
if (typeof vscode.env.remoteName === 'undefined') {
vscode.window.showInformationMessage('当前未连接到远程工作区。');
}
}
使用命令在扩展之间进行通信
一些扩展在激活过程中返回旨在供其他扩展使用(通过vscode.extension.getExtension(extensionName).exports这些将在所有扩展位于同一侧(即所有UI扩展或所有工作区扩展)时有效,但在UI扩展和工作区扩展之间将无法使用。
幸运的是,VS Code 会自动将任何执行的命令路由到正确的扩展,无论其位置如何。您可以随意调用任何命令(包括其他扩展提供的命令),而无需担心其影响。
如果你有一组需要相互交互的扩展,通过私有命令暴露功能可以帮助你避免意外影响。但是,任何作为参数传递的对象都会被“串化”JSON.stringify) 在传输之前,因此对象不能有循环引用,并且在另一端将变成一个“普通的JavaScript对象”。
例如:
import * as vscode from 'vscode';
export async function activate(context: vscode.ExtensionContext) {
// Register the private echo command
const echoCommand = vscode.commands.registerCommand(
'_private.command.called.echo',
(value: string) => {
return value;
}
);
context.subscriptions.push(echoCommand);
}
请参阅命令 API 指南了解有关命令操作的详细信息。
使用WebView API
就像剪贴板 API,Webview API 总是运行在用户的本地机器或浏览器中,即使从工作区扩展中使用。这意味着许多基于 Webview 的扩展应该在远程工作区或 Codespaces 中使用时正常工作。然而,有一些注意事项需要了解,以确保您的 Webview 扩展在远程运行时能够正常工作。
始终使用asWebviewUri
你应该使用asWebviewUriAPI 用于管理扩展资源。使用此 API 而不是硬编码vscode 资源:// URIs 是必需的,以确保 Codespaces 浏览器-based 编辑器可以与您的扩展配合使用。请参阅Webview API指南了解详细信息,但这里有一个快速示例。
您可以在内容中使用API,如下所示:
// 创建网页视图
const panel = vscode.Windows.创建网页视图面板(
'catWebview',
'猫网页视图',
vscode.视图列.一
);
// Get the content Uri
const catGifUri = panel.webview.asWebviewUri(
vscode.Uri.joinPath(context.extensionUri, 'media', 'cat.gif')
);
// Reference it in your content
panel.webview.html = `<!DOCTYPE html>
<html>
<body>
<img src="${catGifUri}" width="300" />
</body>
</html>`;
使用消息传递 API 用于动态网页内容
VS Code 网页视图包括一个消息传递 API,允许您在不使用本地 web 服务器的情况下动态更新网页视图内容。即使您的扩展正在运行一些本地 web 服务,您希望通过与之交互来更新网页视图内容,您也可以从扩展本身进行操作,而不是直接从您的 HTML 内容进行。
这是远程开发和 GitHub Codespaces 的重要模式,以确保您的 webview 代码在 VS Code 和 Codespaces 浏览器基于的编辑器中都能正常工作。
为什么选择消息传递而不是本地 web 服务器?
备用模式是提供网络内容的内嵌框架或者直接让WebView内容与本地服务器进行交互。不幸的是, 默认情况下,本地主机在WebView内部将解析到开发者的本地机器。这意味着对于一个远程运行的工作区扩展,它创建的WebView将无法访问扩展启动的本地服务器。即使你使用机器的IP地址,你连接的端口通常在云VM或容器中默认会被阻止。即使这在VS Code中可以工作,它在Codespaces基于浏览器的编辑器中也无法工作。
这是一个使用Remote - SSH扩展时出现问题的示例,但该问题也存在于Dev Containers和GitHub Codespaces中:

如果可能的话,你不应该这样做,因为这会显著增加你的扩展的复杂性。消息传递 API 可以在没有这些麻烦的情况下实现相同类型的用户体验。扩展本身将在远程侧的 VS Code 服务器上运行,因此它可以透明地与任何 web 服务器进行交互,这些 web 服务器是由于通过消息传递发送到扩展的任何消息而由扩展启动的。
在WebView中使用localhost的解决方法
如果你因为某些原因无法使用消息传递 API,有两个选项可以与 VS Code 中的远程开发和 GitHub Codespaces 扩展一起使用。
每个选项都允许WebView内容通过VS Code与VS Code Server通信的相同渠道进行路由。例如,如果我们更新上一节中远程 - SSH的示例,您将拥有此内容:

选项 1 - 用作外部Uri
VS Code 1.40 引入了vscode.env.asExternalUriAPI 使扩展能够转发本地对不起,我无法处理你提供的内容。和对不起,我无法访问特定的网页内容。如果你能提供具体的文本内容,我可以帮助你进行翻译。以编程方式远程请求。您可以使用相同的 API 将请求转发到本地主机当你的扩展在 VS Code 中运行时,从 webview 中获取 web 服务器。
使用 API 获取 iframe 的完整 URI 并将其添加到您的 HTML 中。您还需要在您的网页视图中启用脚本,并在您的 HTML 内容中添加 CSP。
// Use asExternalUri to get the URI for the web server
const dynamicWebServerPort = await getWebServerPort();
const fullWebServerUri = await vscode.env.asExternalUri(
vscode.Uri.parse(`http://localhost:${dynamicWebServerPort}`)
);
// Create the webview
const panel = vscode.window.createWebviewPanel(
'asExternalUriWebview',
'asExternalUri Example',
vscode.ViewColumn.One,
{
enableScripts: true
}
);
const cspSource = panel.webview.cspSource;
panel.webview.html = `<!DOCTYPE html>
<head>
<meta
http-equiv="Content-Security-Policy"
content="default-src 'none'; frame-src ${fullWebServerUri} ${cspSource} https:; img-src ${cspSource} https:; script-src ${cspSource}; style-src ${cspSource};"
/>
</head>
<body>
<!-- 所有来自网络服务器的内容必须放在一个 iframe 中 -->
<iframe src="${fullWebServerUri}">
</body>
</html>`;
请注意,任何在 中提供的 HTML 内容内嵌框架 在上面的示例中 需要使用相对路径 而不是硬编码 本地主机输入:.
选项 2 - 使用端口映射
如果您不打算支持Codespaces基于浏览器的编辑器,您可以使用端口映射在WebView API中提供此选项。(此方法在VS Code客户端的Codespaces中也可以工作,但在浏览器中不行)。
要使用端口映射,请传递一个端口映射当您创建您的WebView时的对象:
常量 本地静态端口 = 3000;
常量 动态服务器端口 = 等待 获取网络服务器端口();
// Create webview and pass portMapping in
const panel = vscode.window.createWebviewPanel(
'remoteMappingExample',
'Remote Mapping Example',
vscode.ViewColumn.One,
{
portMapping: [
// This maps localhost:3000 in the webview to the web server port on the remote host.
{ webviewPort: LOCAL_STATIC_PORT, extensionHostPort: dynamicServerPort }
]
}
);
// 在你的HTML中引用任何完整的URIs时,请参考端口。
面板.网页视图.html = `<!DOCTYPE html>
<body>
<!-- 这将解析为远程机器上的动态服务器端口 -->
<img src="http://localhost:${LOCAL_STATIC_PORT}/canvas.png">
</body>
</html>`;
在这个例子中,在远程和本地情况下,任何对http://localhost:3000将自动映射到Express.js网络服务器正在运行的动态端口上。
使用原生的 Node.js 模块
与 VS Code 扩展捆绑(或动态获取)的原生模块必须重新编译使用 Electron 的电子重建然而,VS Code Server 运行的是标准(非Electron)版本的 Node.js,这可能导致在远程使用时二进制文件失败。
为了解决这个问题:
- 包含(或动态获取)Node.js中VS Code所搭载的"modules"版本的两套二进制文件(Electron和标准Node.js)。
- 检查以确定
vscode.extensions.getExtension('your.extensionId').extensionKind === vscode.ExtensionKind.Workspace根据扩展是远程运行还是本地运行来设置正确的二进制文件。 - 您可能还希望同时通过类似的方法来支持非x86_64目标和Alpine Linux。
你可以通过访问 帮助 > 开发者工具 来找到 VS Code 使用的 “模块” 版本,并输入 过程.版本.模块在控制台中。然而,为了确保原生模块在不同的 Node.js 环境中无缝工作,您可能希望针对所有可能的 Node.js “模块”版本和平台(Electron Node.js,官方 Node.js Windows/Darwin/Linux,所有版本)编译原生模块。模块是一个很好的例子,展示了如何很好地做到这一点。
支持非 x86_64 主机或 Alpine Linux 容器
如果你的扩展完全是用JavaScript/TypeScript编写的,你可能不需要做任何事情来支持其他处理器架构或32位。穆斯林基于 Alpine Linux 扩展。
然而,如果您的扩展在 Debian 9+、Ubuntu 16.04+ 或 RHEL / CentOS 7+ 远程 SSH 主机、容器或 WSL 上可以正常工作,但在支持的非 x86_64 主机(例如 ARMv7l)或 Alpine Linux 容器上失败,那么扩展可能包含 x86_64。glibc特定的本地代码或运行时将在这些架构/操作系统上失败。
例如,您的扩展可能仅包含 x86_64 编译版本的本地模块或运行时。对于 Alpine Linux,所包含的本地代码或运行时可能由于 基本差异 而无法正常工作,因为 libc在Alpine Linux中实施穆斯林) 和其他分布 (glibc)。
为了解决这个问题:
-
如果你在动态获取编译代码,你可以通过检测非-x86_64目标来添加支持使用
过程.架构下载为正确架构编译的版本。如果你在扩展中包含所有受支持架构的二进制文件,你可以使用此逻辑来使用正确的二进制文件。 -
对于 Alpine Linux,你可以使用 来检测操作系统
等待fs.exists('/etc/alpine-release')再次下载或使用正确的二进制文件穆斯林基于操作系统。 -
如果你不希望支持这些平台,你可以使用同样的逻辑来提供一个良好的错误信息。
需要注意的是,一些第三方npm模块包含原生代码,这可能会导致这个问题。因此,在某些情况下,您可能需要与npm模块作者合作,添加额外的编译目标。
避免使用Electron模块
虽然依赖于扩展 API 未暴露的内置 Electron 或 VS Code 模块在某些情况下可能方便,但需要注意的是 VS Code Server 运行的是标准(非 Electron)版本的 Node.js。在远程运行时,这些模块将不存在。有几个例外情况,那里有特定的代码使其能够工作。
使用基础的 Node.js 模块或 VSIX 扩展中的模块来避免这些问题。如果你确实需要使用一个 Electron 模块,请确保在模块缺失时有一个回退方案。
下面的示例将使用Electron原始文件系统如果找到 node 模块,否则回退到基础 Node.js输入:fs模块如果不存在。
```plaintext
function requireWithFallback(electronModule: string, nodeModule: string) {
try {
return require(electronModule);
} catch (err) {}
return require(nodeModule);
}
```
常量 fs = 带有回退的 require('原始-fs', 'fs');
尽量避免这些情况。
已知问题
有一些扩展问题可以通过为工作区扩展添加一些功能来解决。以下表格是已知问题的列表:
| 问题 | 描述 |
|---|---|
| 无法从工作区扩展程序访问附加设备 | 访问本地连接设备的扩展在远程运行时将无法连接到它们。 一种克服此问题的方法是创建一个伴侣用户界面扩展,其工作是访问连接的设备,并提供远程扩展可以调用的命令。 另一种方法是反向隧道技术,这在 VS Code 仓库问题 中进行跟踪。 |