支援遠端開發和 GitHub Codespaces
Visual Studio Code 遠端開發 允許您透明地與位於其他計算機(無論是虛擬還是物理)上的原始碼和執行時環境進行互動。GitHub Codespaces 是一項服務,它透過託管的雲環境擴充套件了這些功能,這些環境可從 VS Code 和基於瀏覽器的編輯器進行訪問。
為了確保效能,遠端開發和 GitHub Codespaces 都透明地在遠端執行某些 VS Code 擴充套件。但是,這可能會對擴充套件的工作方式產生微妙的影響。雖然許多擴充套件無需修改即可正常工作,但您可能需要進行一些更改,以確保您的擴充套件在所有環境中都能正常工作,儘管這些更改通常很小。
本文件總結了擴充套件作者需要了解的關於遠端開發和 Codespaces 的內容,包括擴充套件 架構,如何在遠端工作區或 Codespaces 中 除錯擴充套件,以及關於 擴充套件無法正常工作時該怎麼辦 的建議。
架構和擴充套件種類
為了儘可能地使遠端開發或 Codespaces 對使用者透明,VS Code 區分了兩種擴充套件:
-
UI 擴充套件:這些擴充套件為 VS Code 使用者介面做出貢獻,並始終在使用者的本地計算機上執行。UI 擴充套件無法直接訪問遠端工作區中的檔案,也無法執行該工作區或計算機上安裝的指令碼/工具。例如,UI 擴充套件包括:主題、程式碼片段、語言語法和鍵盤對映。
-
工作區擴充套件:這些擴充套件在工作區所在的位置執行。在本地工作區中,工作區擴充套件在本地計算機上執行。在遠端工作區中或使用 Codespaces 時,工作區擴充套件在遠端計算機/環境中執行。工作區擴充套件可以訪問工作區中的檔案,以提供豐富的多檔案語言服務、偵錯程式支援,或對工作區中的多個檔案執行復雜操作(直接執行或透過呼叫指令碼/工具)。雖然工作區擴充套件不側重於修改 UI,但它們也可以貢獻資源管理器、檢視和其他 UI 元素。
當用戶安裝擴充套件時,VS Code 會根據其種類自動將其安裝到正確的位置。如果擴充套件可以作為任一種類執行,VS Code 將嘗試為當前情況選擇最佳種類;UI 擴充套件將在 VS Code 的 本地擴充套件主機 中執行,而工作區擴充套件將在 **遠端擴充套件主機** 中執行,該主機位於一個小的 VS Code 伺服器 中(如果遠端工作區存在),否則將在本地擴充套件主機中執行(如果本地存在)。為了確保最新的 VS Code 客戶端功能可用,伺服器需要與 VS Code 客戶端版本完全匹配。因此,當您在容器、遠端 SSH 主機、使用 Codespaces 或在 Linux 子系統 (WSL) 中開啟資料夾時,遠端開發或 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 的預設映像應該包含大多數擴充套件所需的所有先決條件,但您可以在新的 VS Code 終端視窗中安裝任何其他必需的依賴項(例如,使用
yarn install或sudo apt-get)(⌃⇧` (Windows、Linux Ctrl+Shift+`))。 -
最後,按 F5 或使用 **執行和除錯** 檢視在 codespace 中啟動擴充套件。
注意:您將無法在出現的視窗中開啟擴充套件原始檔夾,但您可以開啟 codespace 中的子資料夾或其他位置。
出現的擴充套件開發主機視窗將包含在 codespace 中執行的擴充套件,並且偵錯程式已附加到它。
在自定義開發容器中進行除錯
請按照以下步驟操作:
-
要在本地使用開發容器,請 安裝和配置 Dev Containers 擴充套件,並使用 **檔案 > 開啟... / 開啟資料夾...** 在 VS Code 中本地開啟您的原始碼。要改用 Codespaces,請導航到 GitHub 上包含您擴充套件的儲存庫,並 在 codespace 中開啟它,以便在基於瀏覽器的編輯器中使用它。如果願意,您也可以 在 VS Code 中開啟 codespace。
-
從命令面板(F1)中選擇 **Dev Containers: Add Dev Container Configuration Files...** 或 **Codespaces: Add Dev Container Configuration Files...**,然後選擇 **Node.js & TypeScript**(如果您不使用 TypeScript,則選擇 Node.js)來新增所需的容器配置檔案。
-
可選:在此命令執行後,您可以修改
.devcontainer資料夾的內容,以包含其他構建或執行時要求。有關詳細資訊,請參閱深入的 建立開發容器 文件。 -
執行 **Dev Containers: Reopen in Container** 或 **Codespaces: Add Dev Container Configuration Files...**,片刻之後,VS Code 將設定容器並連線。現在,您將能夠像在本地一樣在容器內開發原始碼。
-
在新 VS Code 終端視窗中執行
yarn install或npm install(⌃⇧` (Windows、Linux Ctrl+Shift+`)),以確保安裝了 Node.js 原生依賴項的 Linux 版本。您也可以安裝其他作業系統或執行時依賴項,但最好也將它們新增到.devcontainer/Dockerfile中,以便在重新構建容器時可用。 -
最後,按 F5 或使用 **執行和除錯** 檢視在此容器中啟動擴充套件並附加偵錯程式。
注意:您將無法在出現的視窗中開啟擴充套件原始檔夾,但您可以開啟容器中的子資料夾或其他位置。
出現的擴充套件開發主機視窗將包含在步驟 2 中定義的容器中執行的擴充套件,並且偵錯程式已附加到它。
使用 SSH 進行除錯
按照步驟進行
-
在 安裝和配置 Remote - SSH 擴充套件後,從 VS Code 中的命令面板(F1)中選擇 **Remote-SSH: Connect to Host...** 來連線到主機。
-
連線後,使用 **檔案 > 開啟... / 開啟資料夾...** 選擇遠端擴充套件原始檔夾,或從命令面板(F1)中選擇 **Git: Clone** 來克隆並在遠端主機上開啟它。
-
在新 VS Code 終端視窗中安裝任何可能缺失的必需依賴項(例如,使用
yarn install或apt-get)(⌃⇧` (Windows、Linux Ctrl+Shift+`))。 -
最後,按 F5 或使用 **執行和除錯** 檢視在遠端主機上啟動擴充套件並附加偵錯程式。
注意:您將無法在出現的視窗中開啟擴充套件原始檔夾,但您可以開啟 SSH 主機中的子資料夾或其他位置。
出現的擴充套件開發主機視窗將包含在 SSH 主機上執行的擴充套件,並且偵錯程式已附加到它。
使用 WSL 進行除錯
請按照以下步驟操作:
-
在 安裝和配置 WSL 擴充套件後,從 VS Code 中的命令面板(F1)中選擇 **WSL: New Window**。
-
在出現的視窗中,使用 **檔案 > 開啟... / 開啟資料夾...** 選擇遠端擴充套件原始檔夾,或從命令面板(F1)中選擇 **Git: Clone** 來克隆並在 WSL 中開啟它。
提示:您可以選擇
/mnt/c資料夾來訪問 Windows 上的任何克隆的原始碼。 -
在新 VS Code 終端視窗中安裝任何可能缺失的必需依賴項(例如,使用
apt-get)(⌃⇧` (Windows、Linux Ctrl+Shift+`))。您至少需要執行yarn install或npm install來確保 Linux 版本的 Node.js 原生依賴項可用。 -
最後,按 F5 或使用 **執行和除錯** 檢視啟動擴充套件並像本地一樣附加偵錯程式。
注意:您將無法在出現的視窗中開啟擴充套件原始檔夾,但您可以開啟 WSL 中的子資料夾或其他位置。
出現的擴充套件開發主機視窗將包含在 WSL 中執行的擴充套件,並且偵錯程式已附加到它。
安裝擴充套件的開發版本
任何時候 VS Code 在 SSH 主機、容器或 WSL 中或透過 GitHub Codespaces 自動安裝擴充套件時,都會使用 Marketplace 版本(而不是您本地計算機上已安裝的版本)。
雖然這在大多數情況下是合理的,但您可能希望測試(或共享)擴充套件的未釋出版本,而無需設定除錯環境。要安裝擴充套件的未釋出版本,您可以將擴充套件打包為 VSIX 並將其手動安裝到已連線到正在執行的遠端環境的 VS Code 視窗中。
請按照以下步驟操作:
- 如果這是一個已釋出的擴充套件,您可能希望在
settings.json中新增"extensions.autoUpdate": false,以防止它自動更新到最新的 Marketplace 版本。 - 接下來,使用
vsce package將您的擴充套件打包為 VSIX。 - 連線到 codespace、Dev Containers、SSH 主機或 WSL 環境。
- 使用擴充套件檢視 **更多操作**(
...)選單中可用的 **從 VSIX 安裝...** 命令,將擴充套件安裝到此特定視窗(而不是本地視窗)。 - 提示時重新載入。
提示:安裝後,您可以使用 **Developer: Show Running Extensions** 命令來檢視 VS Code 是在本地還是遠端執行擴充套件。
處理遠端擴充套件的依賴項
擴充套件可以依賴其他擴充套件的 API。例如:
- 擴充套件可以在其
activate函式中匯出 API。 - 該 API 將可供在同一擴充套件主機中執行的所有擴充套件使用。
- 使用者擴充套件在其
package.json中使用extensionDependencies屬性宣告它們依賴於提供擴充套件。
當所有擴充套件都本地執行並共享同一擴充套件主機時,擴充套件依賴項可以正常工作。
在處理遠端場景時,有可能一個在遠端執行的擴充套件依賴於一個在本地執行的擴充套件。例如,本地擴充套件公開了一個對遠端擴充套件功能至關重要的命令。在這種情況下,我們建議遠端擴充套件將本地擴充套件宣告為 extensionDependency,但問題是擴充套件執行在兩個不同的擴充套件主機上,這意味著提供者的 API 對使用者不可用。因此,要求提供擴充套件完全放棄匯出任何 API 的能力,透過在其擴充套件的 package.json 中使用 "api": "none"。擴充套件仍然可以透過 VS Code 命令(非同步)進行通訊。
這可能看起來是對提供擴充套件不必要的嚴格限制,但使用 "api": "none" 的擴充套件只放棄了從其 activate 方法返回 API 的能力。在其他擴充套件主機上執行的使用者擴充套件仍然可以依賴它們,並且將被啟用。
常見問題
VS Code 的 API 設計成無論您的擴充套件位於何處,都能在正確的位置自動執行。考慮到這一點,有一些 API 可以幫助您避免意外行為。
錯誤的執行位置
如果您的擴充套件功能不符合預期,可能是因為它在錯誤的位置執行。最常見的情況是,您期望擴充套件僅在本地執行,但它卻在遠端執行。您可以使用命令面板(F1)中的 **Developer: Show Running Extensions** 命令來檢視擴充套件的執行位置。
如果 **Developer: Show Running Extensions** 命令顯示 UI 擴充套件被錯誤地視為工作區擴充套件或反之,請嘗試在擴充套件的 package.json 中設定 extensionKind 屬性,如 擴充套件種類部分所述。
您可以使用 remote.extensionKind 設定 快速 **測試** 更改擴充套件種類的效果。此設定是擴充套件 ID 到擴充套件種類的對映。例如,如果您想強制 Azure Databases 擴充套件成為 UI 擴充套件(而不是其預設的工作區擴充套件),並將 Remote - SSH: Editing Configuration Files 擴充套件成為工作區擴充套件(而不是其預設的 UI 擴充套件),您將設定:
{
"remote.extensionKind": {
"ms-azuretools.vscode-cosmosdb": ["ui"],
"ms-vscode-remote.remote-ssh-edit": ["workspace"]
}
}
使用 remote.extensionKind 可以讓您快速測試已釋出的擴充套件版本,而無需修改它們的 package.json 並重新構建它們。
持久化擴充套件資料或狀態
在某些情況下,您的擴充套件可能需要持久化不屬於 settings.json 或單獨的工作區配置檔案(例如 .eslintrc)的狀態資訊。要解決此問題,VS Code 在啟用您的擴充套件時傳入的 vscode.ExtensionContext 物件上提供了一組有用的儲存屬性。如果您的擴充套件已經利用了這些屬性,無論它在何處執行,都應該繼續正常工作。
但是,如果您的擴充套件依賴於當前 VS Code 的路徑約定(例如 ~/.vscode)或特定 OS 資料夾(例如 Linux 上的 ~/.config/Code)的存在來持久化資料,則可能會遇到問題。幸運的是,更新您的擴充套件並避免這些挑戰應該很簡單。
如果您持久化簡單的鍵值對,您可以使用 vscode.ExtensionContext.workspaceState 或 vscode.ExtensionContext.globalState 分別儲存工作區特定或全域性狀態資訊。如果您的資料比鍵值對更復雜,globalStorageUri 和 storageUri 屬性提供了“安全”的 URI,您可以使用它們在檔案中讀取/寫入全域性工作區特定的資訊。
使用 API
import * as vscode from '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)
}
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 將狀態提供給 Settings Sync。這有助於避免在多臺機器上向用戶顯示相同的歡迎或更新頁面。
在 擴充套件功能 主題中有一個使用 setKeysforSync 的示例。
持久化敏感資訊
如果您的擴充套件需要持久化密碼或其他敏感資訊,您可能需要使用 Visual Studio Code 的 SecretStorage API,它提供了一種安全地將文字儲存在檔案系統上的方法,並由加密支援。例如,在桌面端,我們使用 Electron 的 safeStorage API 在將敏感資訊儲存在檔案系統之前對其進行加密。該 API 始終在客戶端儲存敏感資訊,但無論您的擴充套件在何處執行,您都可以使用此 API 並檢索相同的敏感資訊值。
注意:此 API 是持久化密碼和敏感資訊的推薦方法。您不應該使用
vscode.ExtensionContext.workspaceState或vscode.ExtensionContext.globalState儲存敏感資訊,因為這些 API 以明文形式儲存資料。
這是一個示例
import * as vscode from 'vscode';
export function activate(context: vscode.ExtensionContext) {
// ...
const myApiKey = context.secrets.get('apiKey');
// ...
context.secrets.delete('apiKey');
// ...
context.secrets.store('apiKey', myApiKey);
}
使用剪貼簿
歷史上,擴充套件作者使用 clipboardy 等 Node.js 模組與剪貼簿進行互動。不幸的是,如果您在工作區擴充套件中使用這些模組,它們將使用遠端剪貼簿而不是使用者的本地剪貼簿。VS Code 剪貼簿 API 解決了這個問題。無論呼叫它的擴充套件型別如何,它始終在本地執行。
在擴充套件中使用 VS Code 剪貼簿 API
import * as vscode from '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();
// Write to clipboard
await vscode.env.clipboard.writeText(
`It looks like you're copying "${text}". Would you like help?`
);
})
);
}
在本地瀏覽器或應用程式中開啟內容
生成程序或使用 opn 等模組啟動瀏覽器或其他應用程式來處理特定 URI,在本地場景中可能效果很好,但工作區擴充套件在遠端執行,這可能導致應用程式在錯誤的一側啟動。VS Code 遠端開發部分封裝了 opn node 模組,以允許現有擴充套件正常工作。您可以使用 URI 呼叫該模組,VS Code 將在該端顯示 URI 的預設應用程式。但是,這不是一個完整的實現,因為選項不受支援,並且不返回 child_process 物件。
我們建議擴充套件利用 vscode.env.openExternal 方法來啟動本地作業系統上給定 URI 的預設註冊應用程式,而不是依賴第三方 node 模組。更好的是,vscode.env.openExternal 自動進行本地埠轉發! 您可以使用它來指向遠端計算機或 codespace 上的本地 Web 伺服器,即使該埠在外部被阻止,也可以提供內容。
注意:目前 Codespaces 基於瀏覽器的編輯器中的轉發機制僅支援HTTP 和 HTTPS 請求。但是,當從 VS Code 連線到 codespace 時,您可以與任何 TCP 連線進行互動。
使用 vscode.env.openExternal API
import * as vscode from '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://vscode.com.tw'));
// Example 2 - Open an auto-forwarded localhost HTTP server.
vscode.env.openExternal(vscode.Uri.parse('https://:3000'));
// Example 3 - Open the default email application.
vscode.env.openExternal(vscode.Uri.parse('mailto:<fill in your email here>'));
})
);
}
轉發本地主機
雖然 vscode.env.openExternal 中的本地主機轉發機制很有用,但也可能存在您希望在不實際啟動新瀏覽器視窗或應用程式的情況下轉發某些內容的情況。這時 vscode.env.asExternalUri API 就派上用場了。
注意:目前 Codespaces 基於瀏覽器的編輯器中的轉發機制僅支援HTTP 和 HTTPS 請求。但是,當從 VS Code 連線到 codespace 時,您可以與任何 TCP 連線進行互動。
使用 vscode.env.asExternalUri API
import * as vscode from 'vscode';
import { getExpressServerPort } from './server';
export async function activate(context: vscode.ExtensionContext) {
const dynamicServerPort = await getWebServerPort();
context.subscriptions.push(vscode.commands.registerCommand('myAmazingExtension.forwardLocalhost', async () =>
// Make the port available locally and get the full URI
const fullUri = await vscode.env.asExternalUri(
vscode.Uri.parse(`https://:${dynamicServerPort}`));
// ... do something with the fullUri ...
}));
}
重要的是要注意,API 返回的 URI可能根本不引用 localhost,因此您應該完整地使用它。這對於 Codespaces 的基於瀏覽器的編輯器尤其重要,因為 localhost 在其中無法使用。
回撥和 URI 處理程式
vscode.window.registerUriHandler API 允許您的擴充套件註冊一個自定義 URI,如果該 URI 在瀏覽器中開啟,將觸發您擴充套件中的回撥函式。註冊 URI 處理程式的常見用例是實現與 OAuth 2.0 身份驗證提供者(例如 Azure AD)的服務登入。但是,它也可以用於任何您希望外部應用程式或瀏覽器向您的擴充套件傳送資訊的場景。
VS Code 中的遠端開發和 Codespaces 擴充套件將透明地處理將 URI 傳遞給您的擴充套件,而不管它實際執行在哪裡(本地或遠端)。但是,vscode:// URI 在 Codespaces 的基於瀏覽器的編輯器中將無法正常工作,因為在瀏覽器中開啟這些 URI 會嘗試將它們傳遞給本地 VS Code 客戶端而不是基於瀏覽器的編輯器。幸運的是,這可以透過使用 vscode.env.asExternalUri API 輕鬆解決。
讓我們結合使用 vscode.window.registerUriHandler 和 vscode.env.asExternalUri 來連線一個 OAuth 身份驗證回撥示例
import * as vscode from 'vscode';
// This is ${publisher}.${name} from 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,該 URI 可用作身份驗證提供者的回撥。在 Codespaces 的基於瀏覽器的編輯器中執行時,它會連線一個 https://*.github.dev URI,而無需進行任何程式碼更改或特殊條件。
雖然 OAuth 超出了本文件的範圍,但請注意,如果您將此示例改編為真實的身份驗證提供者,您可能需要構建一個代理服務來處理該提供者。這是因為並非所有提供者都允許 vscode:// 回撥 URI,而另一些則不允許在 HTTPS 回撥中使用萬用字元主機名。我們還建議儘可能使用 OAuth 2.0 授權碼 PKCE 流程(例如,Azure AD 支援 PKCE),以提高回撥的安全性。
遠端執行或在 Codespaces 瀏覽器編輯器中執行時行為各異
在某些情況下,您的工作區擴充套件可能需要在遠端執行時改變行為。在其他情況下,您可能希望在 Codespaces 基於瀏覽器的編輯器中執行時改變其行為。VS Code 提供了三個 API 來檢測這些情況:vscode.env.uiKind、extension.extensionKind 和 vscode.env.remoteName。
接下來,您可以如下使用這三個 API:
import * as vscode from '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 will return undefined for remoteName if working with a local workspace
if (typeof vscode.env.remoteName === 'undefined') {
vscode.window.showInformationMessage('Not currently connected to a remote workspace.');
}
}
使用命令在擴充套件之間進行通訊
一些擴充套件在其啟用時返回 API,這些 API 供其他擴充套件使用(透過 vscode.extension.getExtension(extensionName).exports)。雖然如果所有涉及的擴充套件都在同一側(全部是 UI 擴充套件或全部是工作區擴充套件),這些 API 將會正常工作,但它們在 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
您應該使用 asWebviewUri API 來管理擴充套件資源。使用此 API 而不是硬編碼 vscode-resource:// URI 是確保 Codespaces 的基於瀏覽器的編輯器與您的擴充套件一起工作的要求。有關詳細資訊,請參閱 Webview API 指南,這裡有一個簡短的示例。
您可以在您的內容中如下使用該 API:
// Create the webview
const panel = vscode.window.createWebviewPanel(
'catWebview',
'Cat Webview',
vscode.ViewColumn.One
);
// 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 為動態 Webview 內容
VS Code Webview 包括一個 訊息傳遞 API,允許您動態更新 Webview 內容,而無需使用本地 Web 伺服器。即使您的擴充套件正在執行一些您想與之互動以更新 Webview 內容的本地 Web 服務,您也可以從擴充套件本身執行此操作,而不是直接從 HTML 內容中執行。
這是遠端開發和 GitHub Codespaces 的一個重要模式,以確保您的 Webview 程式碼能在 VS Code 和 Codespaces 的基於瀏覽器的編輯器中正常工作。
為什麼使用訊息傳遞而不是 localhost Web 伺服器?
備用模式是透過在 iframe 中提供 Web 內容,或者讓 webview 內容直接與本地主機伺服器進行互動。不幸的是,預設情況下,webview 中的 localhost 會解析到開發者的本地機器。這意味著,對於遠端執行的工作區擴充套件,它建立的 webview 將無法訪問擴充套件啟動的本地伺服器。即使您使用機器的 IP,預設情況下,雲虛擬機器或容器中的埠也通常會被阻止。即使這在 VS Code 中有效,在基於瀏覽器的 Codespaces 編輯器中也無效。
當使用 Remote - SSH 擴充套件時,這裡有一個問題的說明,但這個問題也存在於 Dev Containers 和 GitHub Codespaces 中。

如果可能,**您應該避免這樣做**,因為它會顯著地使您的擴充套件複雜化。訊息傳遞 API 可以在沒有這些麻煩的情況下實現相同的使用者體驗。擴充套件本身將在遠端的 VS Code Server 中執行,因此它可以透明地與您的擴充套件響應 webview 傳遞給它的任何訊息而啟動的任何 Web 伺服器進行互動。
從 webview 使用 localhost 的解決方法
如果出於某種原因無法使用訊息傳遞 API,有兩種選項可以在 VS Code 中與 Remote Development 和 GitHub Codespaces 擴充套件一起使用。
每個選項都允許 webview 內容透過 VS Code 用於與 VS Code Server 通訊的相同通道進行路由。例如,如果我們更新上一節中 Remote - SSH 的說明圖,您將得到以下內容:

選項 1 - 使用 asExternalUri
VS Code 1.40 引入了 vscode.env.asExternalUri API,允許擴充套件以程式設計方式將本地 http 和 https 請求遠端轉發。當您的擴充套件在 VS Code 中執行時,您可以使用此 API 將請求從 webview 轉發到 localhost Web 伺服器。
使用該 API 獲取 iframe 的完整 URI 並將其新增到 HTML 中。您還需要在 webview 中啟用指令碼,併為 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(`https://:${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>
<!-- All content from the web server must be in an iframe -->
<iframe src="${fullWebServerUri}">
</body>
</html>`;
請注意,上面示例中 iframe 中提供的任何 HTML 內容**都需要使用相對路徑**,而不是硬編碼 localhost。
選項 2 - 使用埠對映
如果您**無意支援基於瀏覽器的 Codespaces 編輯器**,則可以使用 webview API 中提供的 portMapping 選項。(此方法也可與 VS Code 客戶端的 Codespaces 一起使用,但在瀏覽器中無效)。
要使用埠對映,請在建立 webview 時傳入 portMapping 物件。
const LOCAL_STATIC_PORT = 3000;
const dynamicServerPort = await getWebServerPort();
// 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 }
]
}
);
// Reference the port in any full URIs you reference in your HTML.
panel.webview.html = `<!DOCTYPE html>
<body>
<!-- This will resolve to the dynamic server port on the remote machine -->
<img src="https://:${LOCAL_STATIC_PORT}/canvas.png">
</body>
</html>`;
在此示例中,對於遠端和本地情況,對 https://:3000 的任何請求都將自動對映到 Express.js Web 伺服器正在執行的動態埠。
使用原生 Node.js 模組
隨 VS Code 擴充套件一起打包(或動態獲取)的原生模組必須使用 Electron 的 electron-rebuild 進行重新編譯。然而,VS Code Server 執行標準(非 Electron)版本的 Node.js,這可能導致在遠端使用二進位制檔案時失敗。
要解決此問題
- 在 Node.js 的“modules”版本中包含(或動態獲取)這兩個二進位制檔案集(Electron 和標準 Node.js),VS Code 會隨之釋出。
- 檢查
vscode.extensions.getExtension('your.extensionId').extensionKind === vscode.ExtensionKind.Workspace以根據擴充套件是在遠端還是本地執行來設定正確的二進位制檔案。 - 您可能還希望透過遵循類似的邏輯來同時支援非 x86_64 目標和 Alpine Linux。
您可以透過轉到**幫助 > 開發者工具**並輸入 process.versions.modules 在控制檯中找到 VS Code 使用的“modules”版本。但是,為確保原生模組在不同的 Node.js 環境中無縫工作,您可能希望針對所有可能的 Node.js “modules”版本和您想要支援的平臺(Electron Node.js、官方 Node.js Windows/Darwin/Linux、所有版本)進行編譯。 node-tree-sitter 模組是一個很好的例子,說明了如何做到這一點。
支援非 x86_64 主機或 Alpine Linux 容器
如果您的擴充套件完全是用 JavaScript/TypeScript 編寫的,您可能無需執行任何操作即可為其他處理器架構或基於 musl 的 Alpine Linux 新增對您擴充套件的支援。
但是,如果您的擴充套件在 Debian 9+、Ubuntu 16.04+ 或 RHEL / CentOS 7+ 遠端 SSH 主機、容器或 WSL 上執行,但在支援的非 x86_64 主機(例如 ARMv7l)或 Alpine Linux 容器上失敗,則該擴充套件可能包含 x86_64 glibc 特定的原生程式碼或執行時,這些程式碼在這些架構/作業系統上會失敗。
例如,您的擴充套件可能只包含原生模組或執行時的 x86_64 編譯版本。對於 Alpine Linux,由於 Alpine Linux (musl) 和其他發行版 (glibc) 的 libc 實現方式之間存在根本性差異,因此包含的原生程式碼或執行時可能無法正常工作。
解決此問題
-
如果您動態獲取已編譯的程式碼,可以透過使用
process.arch檢測非 x86_64 目標並下載為正確架構編譯的版本來新增支援。如果您將所有受支援架構的二進位制檔案包含在擴充套件中,則可以使用此邏輯來使用正確的二進位制檔案。 -
對於 Alpine Linux,您可以使用
await fs.exists('/etc/alpine-release')檢測作業系統,並再次下載或使用基於musl的作業系統的正確二進位制檔案。 -
如果您寧願不支援這些平臺,您可以使用相同的邏輯來提供一個良好的錯誤訊息。
需要注意的是,一些第三方 npm 模組包含可能導致此問題的原生程式碼。因此,在某些情況下,您可能需要與 npm 模組作者合作以新增其他編譯目標。
避免使用 Electron 模組
雖然依賴 Electron 內建模組或擴充套件 API 未公開的 VS Code 模組可能很方便,但重要的是要注意,VS Code Server 執行的是標準的(非 Electron)Node.js 版本。在遠端執行時,這些模組將缺失。有幾種例外情況,其中有特定的程式碼使其能夠工作。
使用基於 Node.js 的模組或擴充套件 VSIX 中的模組以避免這些問題。如果您絕對需要使用 Electron 模組,請確保在模組缺失時有回退方案。
下面的示例將使用 Electron original-fs 節點模組(如果找到),否則將回退到基於 Node.js 的 fs 模組。
function requireWithFallback(electronModule: string, nodeModule: string) {
try {
return require(electronModule);
} catch (err) {}
return require(nodeModule);
}
const fs = requireWithFallback('original-fs', 'fs');
儘量避免這些情況。
已知問題
有幾個擴充套件問題可以透過為工作區擴充套件新增一些功能來解決。下表是正在考慮中的已知問題列表。
| 問題 | 描述 |
|---|---|
| 工作區擴充套件無法訪問已連線的裝置 | 訪問本地已連線裝置的擴充套件在遠端執行時將無法連線到它們。克服此問題的一種方法是建立一個伴隨的 UI 擴充套件,該擴充套件負責訪問已連線的裝置並提供遠端擴充套件也可以呼叫的命令。 另一種方法是反向隧道,這正在 VS Code 倉庫問題中跟蹤。 |