805 lines
19 KiB
Markdown
805 lines
19 KiB
Markdown
# VS Code Extension API 核心知识点
|
||
|
||
## 目录
|
||
- [1. Extension 生命周期](#1-extension-生命周期) ⭐⭐⭐
|
||
- [2. 激活事件 (Activation Events)](#2-激活事件-activation-events) ⭐⭐
|
||
- [3. 命令系统 (Commands)](#3-命令系统-commands) ⭐⭐
|
||
- [4. Webview API](#4-webview-api) ⭐⭐⭐⭐⭐ **面试重点**
|
||
- [5. TreeView 和自定义视图](#5-treeview-和自定义视图) ⭐⭐
|
||
- [6. 文件系统操作](#6-文件系统操作) ⭐⭐⭐
|
||
- [7. 配置和存储](#7-配置和存储) ⭐⭐⭐⭐ **面试重点**
|
||
- [8. 消息通知](#8-消息通知) ⭐
|
||
- [9. 语言特性支持](#9-语言特性支持) ⭐
|
||
- [10. 调试和诊断](#10-调试和诊断) ⭐
|
||
|
||
---
|
||
|
||
## 1. Extension 生命周期 ⭐⭐⭐
|
||
|
||
### 1.1 核心函数 🔥必考
|
||
|
||
```typescript
|
||
// extension.ts
|
||
import * as vscode from 'vscode';
|
||
|
||
// 插件激活时调用(只调用一次)
|
||
export function activate(context: vscode.ExtensionContext) {
|
||
console.log('Extension is now active!');
|
||
|
||
// 注册命令、视图、事件监听等
|
||
// 使用 context.subscriptions 管理资源
|
||
}
|
||
|
||
// 插件停用时调用(清理资源)
|
||
export function deactivate() {
|
||
console.log('Extension is deactivated');
|
||
// 清理资源、关闭连接等
|
||
}
|
||
```
|
||
|
||
### 1.2 ExtensionContext 重要属性 🔥必考
|
||
|
||
```typescript
|
||
interface ExtensionContext {
|
||
// 插件订阅管理(自动清理)
|
||
subscriptions: { dispose(): any }[];
|
||
|
||
// 工作区存储路径
|
||
storageUri: vscode.Uri | undefined;
|
||
globalStorageUri: vscode.Uri;
|
||
|
||
// 插件路径
|
||
extensionUri: vscode.Uri;
|
||
extensionPath: string;
|
||
|
||
// 状态存储
|
||
workspaceState: Memento; // 工作区级别
|
||
globalState: Memento; // 全局级别
|
||
secrets: SecretStorage; // 敏感信息存储
|
||
|
||
// 环境变量
|
||
environmentVariableCollection: EnvironmentVariableCollection;
|
||
}
|
||
```
|
||
|
||
### 1.3 资源管理最佳实践 🔥必考
|
||
|
||
```typescript
|
||
export function activate(context: vscode.ExtensionContext) {
|
||
// ✅ 推荐:使用 context.subscriptions 自动管理
|
||
context.subscriptions.push(
|
||
vscode.commands.registerCommand('extension.command', () => {})
|
||
);
|
||
|
||
// ❌ 不推荐:手动管理容易忘记清理
|
||
const disposable = vscode.commands.registerCommand('extension.command', () => {});
|
||
// 需要在 deactivate 中手动调用 disposable.dispose()
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 2. 激活事件 (Activation Events) ⭐⭐
|
||
|
||
### 2.1 常用激活事件 📌重要
|
||
|
||
```json
|
||
// package.json
|
||
{
|
||
"activationEvents": [
|
||
// 启动时激活
|
||
"onStartupFinished",
|
||
|
||
// 执行命令时激活
|
||
"onCommand:extension.helloWorld",
|
||
|
||
// 打开特定语言文件时激活
|
||
"onLanguage:javascript",
|
||
"onLanguage:verilog",
|
||
|
||
// 打开特定文件类型时激活
|
||
"onFileSystem:sftp",
|
||
|
||
// 打开特定视图时激活
|
||
"onView:myCustomView",
|
||
|
||
// 调试时激活
|
||
"onDebug",
|
||
|
||
// 打开特定 URI 时激活
|
||
"onUri",
|
||
|
||
// Webview 恢复时激活
|
||
"onWebviewPanel:myWebview",
|
||
|
||
// 任务执行时激活
|
||
"onTaskType:npm"
|
||
]
|
||
}
|
||
```
|
||
|
||
### 2.2 延迟激活策略 🔥必考
|
||
|
||
```typescript
|
||
// ✅ 推荐:使用 onStartupFinished 延迟激活
|
||
"activationEvents": ["onStartupFinished"]
|
||
|
||
// ❌ 不推荐:使用 * 会拖慢启动速度
|
||
"activationEvents": ["*"]
|
||
```
|
||
|
||
---
|
||
|
||
## 3. 命令系统 (Commands)
|
||
|
||
### 3.1 注册命令
|
||
|
||
```typescript
|
||
// 注册简单命令
|
||
const disposable = vscode.commands.registerCommand(
|
||
'extension.helloWorld',
|
||
() => {
|
||
vscode.window.showInformationMessage('Hello World!');
|
||
}
|
||
);
|
||
context.subscriptions.push(disposable);
|
||
|
||
// 注册带参数的命令
|
||
vscode.commands.registerCommand(
|
||
'extension.openFile',
|
||
(filePath: string) => {
|
||
vscode.workspace.openTextDocument(filePath).then(doc => {
|
||
vscode.window.showTextDocument(doc);
|
||
});
|
||
}
|
||
);
|
||
```
|
||
|
||
### 3.2 执行命令
|
||
|
||
```typescript
|
||
// 执行内置命令
|
||
await vscode.commands.executeCommand('workbench.action.files.save');
|
||
|
||
// 执行自定义命令
|
||
await vscode.commands.executeCommand('extension.openFile', '/path/to/file');
|
||
|
||
// 获取所有可用命令
|
||
const commands = await vscode.commands.getCommands();
|
||
```
|
||
|
||
### 3.3 常用内置命令
|
||
|
||
```typescript
|
||
// 文件操作
|
||
'workbench.action.files.save'
|
||
'workbench.action.files.saveAll'
|
||
'workbench.action.closeActiveEditor'
|
||
|
||
// 编辑器操作
|
||
'editor.action.formatDocument'
|
||
'editor.action.commentLine'
|
||
'editor.action.selectAll'
|
||
|
||
// 窗口操作
|
||
'workbench.action.toggleSidebarVisibility'
|
||
'workbench.action.terminal.new'
|
||
'workbench.action.quickOpen'
|
||
|
||
// Git 操作
|
||
'git.commit'
|
||
'git.push'
|
||
'git.pull'
|
||
```
|
||
|
||
---
|
||
|
||
## 4. Webview API ⭐⭐⭐⭐⭐ **面试重点**
|
||
|
||
### 4.1 创建 Webview Panel 🔥必考
|
||
|
||
```typescript
|
||
const panel = vscode.window.createWebviewPanel(
|
||
'myWebview', // viewType(唯一标识)
|
||
'My Webview', // 标题
|
||
vscode.ViewColumn.One, // 显示位置
|
||
{
|
||
enableScripts: true, // 启用 JavaScript
|
||
retainContextWhenHidden: true, // 隐藏时保留状态
|
||
localResourceRoots: [ // 允许访问的本地资源路径
|
||
vscode.Uri.joinPath(context.extensionUri, 'media')
|
||
]
|
||
}
|
||
);
|
||
```
|
||
|
||
### 4.2 设置 Webview 内容
|
||
|
||
```typescript
|
||
panel.webview.html = getWebviewContent();
|
||
|
||
function getWebviewContent() {
|
||
return `<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>My Webview</title>
|
||
</head>
|
||
<body>
|
||
<h1>Hello from Webview!</h1>
|
||
<button onclick="sendMessage()">Send Message</button>
|
||
|
||
<script>
|
||
const vscode = acquireVsCodeApi();
|
||
|
||
function sendMessage() {
|
||
vscode.postMessage({
|
||
command: 'alert',
|
||
text: 'Hello from Webview!'
|
||
});
|
||
}
|
||
|
||
// 接收来自 Extension 的消息
|
||
window.addEventListener('message', event => {
|
||
const message = event.data;
|
||
console.log('Received:', message);
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>`;
|
||
}
|
||
```
|
||
|
||
### 4.3 Webview 消息通信 🔥必考(项目核心)
|
||
|
||
```typescript
|
||
// Extension → Webview
|
||
panel.webview.postMessage({
|
||
command: 'update',
|
||
data: 'some data'
|
||
});
|
||
|
||
// Webview → Extension
|
||
panel.webview.onDidReceiveMessage(
|
||
message => {
|
||
switch (message.command) {
|
||
case 'alert':
|
||
vscode.window.showInformationMessage(message.text);
|
||
break;
|
||
case 'getData':
|
||
// 处理数据请求
|
||
panel.webview.postMessage({
|
||
command: 'dataResponse',
|
||
data: fetchData()
|
||
});
|
||
break;
|
||
}
|
||
},
|
||
undefined,
|
||
context.subscriptions
|
||
);
|
||
```
|
||
|
||
### 4.4 Webview 生命周期管理 📌重要
|
||
|
||
```typescript
|
||
// 监听 Webview 关闭事件
|
||
panel.onDidDispose(
|
||
() => {
|
||
// 清理资源
|
||
console.log('Webview disposed');
|
||
},
|
||
null,
|
||
context.subscriptions
|
||
);
|
||
|
||
// 监听 Webview 可见性变化
|
||
panel.onDidChangeViewState(
|
||
e => {
|
||
if (e.webviewPanel.visible) {
|
||
console.log('Webview is now visible');
|
||
}
|
||
},
|
||
null,
|
||
context.subscriptions
|
||
);
|
||
```
|
||
|
||
### 4.5 加载本地资源 📌重要
|
||
|
||
```typescript
|
||
// 获取本地资源 URI
|
||
const scriptUri = panel.webview.asWebviewUri(
|
||
vscode.Uri.joinPath(context.extensionUri, 'media', 'script.js')
|
||
);
|
||
|
||
const styleUri = panel.webview.asWebviewUri(
|
||
vscode.Uri.joinPath(context.extensionUri, 'media', 'style.css')
|
||
);
|
||
|
||
// 在 HTML 中使用
|
||
const html = `
|
||
<link href="${styleUri}" rel="stylesheet">
|
||
<script src="${scriptUri}"></script>
|
||
`;
|
||
```
|
||
|
||
### 4.6 Webview 状态持久化 📌重要
|
||
|
||
```typescript
|
||
// Webview 中保存状态
|
||
const vscode = acquireVsCodeApi();
|
||
const state = vscode.getState() || { count: 0 };
|
||
|
||
// 更新状态
|
||
state.count++;
|
||
vscode.setState(state);
|
||
|
||
// Extension 中序列化状态
|
||
panel.webview.options = {
|
||
enableScripts: true,
|
||
retainContextWhenHidden: true
|
||
};
|
||
|
||
// 恢复 Webview
|
||
vscode.window.registerWebviewPanelSerializer('myWebview', {
|
||
async deserializeWebviewPanel(webviewPanel, state) {
|
||
webviewPanel.webview.html = getWebviewContent();
|
||
// 恢复状态
|
||
webviewPanel.webview.postMessage({ command: 'restore', state });
|
||
}
|
||
});
|
||
```
|
||
|
||
---
|
||
|
||
## 5. TreeView 和自定义视图
|
||
|
||
### 5.1 创建 TreeView Provider
|
||
|
||
```typescript
|
||
class MyTreeDataProvider implements vscode.TreeDataProvider<TreeItem> {
|
||
private _onDidChangeTreeData = new vscode.EventEmitter<TreeItem | undefined>();
|
||
readonly onDidChangeTreeData = this._onDidChangeTreeData.event;
|
||
|
||
refresh(): void {
|
||
this._onDidChangeTreeData.fire(undefined);
|
||
}
|
||
|
||
getTreeItem(element: TreeItem): vscode.TreeItem {
|
||
return element;
|
||
}
|
||
|
||
getChildren(element?: TreeItem): Thenable<TreeItem[]> {
|
||
if (!element) {
|
||
// 返回根节点
|
||
return Promise.resolve([
|
||
new TreeItem('Item 1', vscode.TreeItemCollapsibleState.None),
|
||
new TreeItem('Item 2', vscode.TreeItemCollapsibleState.Collapsed)
|
||
]);
|
||
}
|
||
// 返回子节点
|
||
return Promise.resolve([]);
|
||
}
|
||
}
|
||
|
||
class TreeItem extends vscode.TreeItem {
|
||
constructor(
|
||
public readonly label: string,
|
||
public readonly collapsibleState: vscode.TreeItemCollapsibleState
|
||
) {
|
||
super(label, collapsibleState);
|
||
this.tooltip = `Tooltip for ${label}`;
|
||
this.command = {
|
||
command: 'extension.itemClicked',
|
||
title: 'Click Item',
|
||
arguments: [this]
|
||
};
|
||
}
|
||
}
|
||
```
|
||
|
||
### 5.2 注册 TreeView
|
||
|
||
```typescript
|
||
const treeDataProvider = new MyTreeDataProvider();
|
||
const treeView = vscode.window.createTreeView('myTreeView', {
|
||
treeDataProvider,
|
||
showCollapseAll: true
|
||
});
|
||
|
||
context.subscriptions.push(treeView);
|
||
|
||
// 刷新视图
|
||
treeDataProvider.refresh();
|
||
```
|
||
|
||
### 5.3 WebviewView Provider(侧边栏 Webview)
|
||
|
||
```typescript
|
||
class MyWebviewViewProvider implements vscode.WebviewViewProvider {
|
||
resolveWebviewView(
|
||
webviewView: vscode.WebviewView,
|
||
context: vscode.WebviewViewResolveContext,
|
||
token: vscode.CancellationToken
|
||
) {
|
||
webviewView.webview.options = {
|
||
enableScripts: true
|
||
};
|
||
|
||
webviewView.webview.html = getWebviewContent();
|
||
|
||
webviewView.webview.onDidReceiveMessage(message => {
|
||
// 处理消息
|
||
});
|
||
}
|
||
}
|
||
|
||
// 注册
|
||
vscode.window.registerWebviewViewProvider(
|
||
'myWebviewView',
|
||
new MyWebviewViewProvider()
|
||
);
|
||
```
|
||
|
||
---
|
||
|
||
## 6. 文件系统操作 ⭐⭐⭐
|
||
|
||
### 6.1 读取文件 📌重要
|
||
|
||
```typescript
|
||
// 读取文本文件
|
||
const uri = vscode.Uri.file('/path/to/file.txt');
|
||
const content = await vscode.workspace.fs.readFile(uri);
|
||
const text = Buffer.from(content).toString('utf8');
|
||
|
||
// 使用 TextDocument API
|
||
const document = await vscode.workspace.openTextDocument(uri);
|
||
const text = document.getText();
|
||
```
|
||
|
||
### 6.2 写入文件
|
||
|
||
```typescript
|
||
// 写入文件
|
||
const uri = vscode.Uri.file('/path/to/file.txt');
|
||
const content = Buffer.from('Hello World', 'utf8');
|
||
await vscode.workspace.fs.writeFile(uri, content);
|
||
|
||
// 使用 WorkspaceEdit
|
||
const edit = new vscode.WorkspaceEdit();
|
||
edit.createFile(uri, { overwrite: true });
|
||
edit.insert(uri, new vscode.Position(0, 0), 'Hello World');
|
||
await vscode.workspace.applyEdit(edit);
|
||
```
|
||
|
||
### 6.3 文件监听
|
||
|
||
```typescript
|
||
// 监听文件变化
|
||
const watcher = vscode.workspace.createFileSystemWatcher('**/*.js');
|
||
|
||
watcher.onDidCreate(uri => {
|
||
console.log('File created:', uri.fsPath);
|
||
});
|
||
|
||
watcher.onDidChange(uri => {
|
||
console.log('File changed:', uri.fsPath);
|
||
});
|
||
|
||
watcher.onDidDelete(uri => {
|
||
console.log('File deleted:', uri.fsPath);
|
||
});
|
||
|
||
context.subscriptions.push(watcher);
|
||
```
|
||
|
||
### 6.4 工作区操作
|
||
|
||
```typescript
|
||
// 获取工作区文件夹
|
||
const workspaceFolders = vscode.workspace.workspaceFolders;
|
||
if (workspaceFolders) {
|
||
const rootPath = workspaceFolders[0].uri.fsPath;
|
||
}
|
||
|
||
// 查找文件
|
||
const files = await vscode.workspace.findFiles(
|
||
'**/*.ts', // include pattern
|
||
'**/node_modules/**' // exclude pattern
|
||
);
|
||
|
||
// 打开文件
|
||
const document = await vscode.workspace.openTextDocument(uri);
|
||
await vscode.window.showTextDocument(document);
|
||
```
|
||
|
||
---
|
||
|
||
## 7. 配置和存储 ⭐⭐⭐⭐ **面试重点**
|
||
|
||
### 7.1 读取配置 📌重要
|
||
|
||
```typescript
|
||
// 读取配置
|
||
const config = vscode.workspace.getConfiguration('myExtension');
|
||
const value = config.get<string>('settingName', 'defaultValue');
|
||
|
||
// 监听配置变化
|
||
vscode.workspace.onDidChangeConfiguration(e => {
|
||
if (e.affectsConfiguration('myExtension.settingName')) {
|
||
console.log('Configuration changed');
|
||
}
|
||
});
|
||
```
|
||
|
||
### 7.2 更新配置
|
||
|
||
```typescript
|
||
const config = vscode.workspace.getConfiguration('myExtension');
|
||
|
||
// 更新用户配置(全局)
|
||
await config.update('settingName', 'newValue', vscode.ConfigurationTarget.Global);
|
||
|
||
// 更新工作区配置
|
||
await config.update('settingName', 'newValue', vscode.ConfigurationTarget.Workspace);
|
||
```
|
||
|
||
### 7.3 状态存储 🔥必考
|
||
|
||
```typescript
|
||
// 工作区状态(仅当前工作区)
|
||
await context.workspaceState.update('key', 'value');
|
||
const value = context.workspaceState.get('key');
|
||
|
||
// 全局状态(跨工作区)
|
||
await context.globalState.update('key', 'value');
|
||
const value = context.globalState.get('key');
|
||
|
||
// 存储对象
|
||
await context.globalState.update('userData', { name: 'John', age: 30 });
|
||
```
|
||
|
||
### 7.4 敏感信息存储 🔥必考(Token 管理)
|
||
|
||
```typescript
|
||
// 存储密码、Token 等敏感信息
|
||
await context.secrets.store('apiToken', 'secret-token-value');
|
||
|
||
// 读取
|
||
const token = await context.secrets.get('apiToken');
|
||
|
||
// 删除
|
||
await context.secrets.delete('apiToken');
|
||
|
||
// 监听变化
|
||
context.secrets.onDidChange(e => {
|
||
console.log('Secret changed:', e.key);
|
||
});
|
||
```
|
||
|
||
---
|
||
|
||
## 8. 消息通知
|
||
|
||
### 8.1 信息提示
|
||
|
||
```typescript
|
||
// 普通信息
|
||
vscode.window.showInformationMessage('Operation completed!');
|
||
|
||
// 警告
|
||
vscode.window.showWarningMessage('This action may cause issues');
|
||
|
||
// 错误
|
||
vscode.window.showErrorMessage('Operation failed!');
|
||
```
|
||
|
||
### 8.2 带按钮的提示
|
||
|
||
```typescript
|
||
const result = await vscode.window.showInformationMessage(
|
||
'Do you want to continue?',
|
||
'Yes',
|
||
'No',
|
||
'Cancel'
|
||
);
|
||
|
||
if (result === 'Yes') {
|
||
// 用户点击了 Yes
|
||
}
|
||
```
|
||
|
||
### 8.3 输入框
|
||
|
||
```typescript
|
||
// 简单输入
|
||
const input = await vscode.window.showInputBox({
|
||
prompt: 'Enter your name',
|
||
placeHolder: 'John Doe',
|
||
validateInput: (value) => {
|
||
return value.length < 3 ? 'Name too short' : null;
|
||
}
|
||
});
|
||
|
||
// 快速选择
|
||
const selected = await vscode.window.showQuickPick(
|
||
['Option 1', 'Option 2', 'Option 3'],
|
||
{
|
||
placeHolder: 'Select an option',
|
||
canPickMany: false
|
||
}
|
||
);
|
||
```
|
||
|
||
### 8.4 进度提示
|
||
|
||
```typescript
|
||
await vscode.window.withProgress(
|
||
{
|
||
location: vscode.ProgressLocation.Notification,
|
||
title: 'Processing...',
|
||
cancellable: true
|
||
},
|
||
async (progress, token) => {
|
||
token.onCancellationRequested(() => {
|
||
console.log('User canceled');
|
||
});
|
||
|
||
progress.report({ increment: 0, message: 'Starting...' });
|
||
await doWork1();
|
||
|
||
progress.report({ increment: 50, message: 'Half done...' });
|
||
await doWork2();
|
||
|
||
progress.report({ increment: 100, message: 'Complete!' });
|
||
}
|
||
);
|
||
```
|
||
|
||
---
|
||
|
||
## 9. 语言特性支持
|
||
|
||
### 9.1 代码补全
|
||
|
||
```typescript
|
||
const provider = vscode.languages.registerCompletionItemProvider(
|
||
'javascript',
|
||
{
|
||
provideCompletionItems(document, position) {
|
||
const item = new vscode.CompletionItem('myFunction');
|
||
item.kind = vscode.CompletionItemKind.Function;
|
||
item.detail = 'My custom function';
|
||
item.documentation = 'This is a custom function';
|
||
item.insertText = new vscode.SnippetString('myFunction($1)$0');
|
||
|
||
return [item];
|
||
}
|
||
},
|
||
'.' // 触发字符
|
||
);
|
||
|
||
context.subscriptions.push(provider);
|
||
```
|
||
|
||
### 9.2 悬停提示
|
||
|
||
```typescript
|
||
const provider = vscode.languages.registerHoverProvider('javascript', {
|
||
provideHover(document, position) {
|
||
const range = document.getWordRangeAtPosition(position);
|
||
const word = document.getText(range);
|
||
|
||
return new vscode.Hover([
|
||
`**${word}**`,
|
||
'This is a hover tooltip'
|
||
]);
|
||
}
|
||
});
|
||
```
|
||
|
||
### 9.3 诊断(错误提示)
|
||
|
||
```typescript
|
||
const diagnosticCollection = vscode.languages.createDiagnosticCollection('myExtension');
|
||
context.subscriptions.push(diagnosticCollection);
|
||
|
||
function updateDiagnostics(document: vscode.TextDocument) {
|
||
const diagnostics: vscode.Diagnostic[] = [];
|
||
|
||
const text = document.getText();
|
||
const regex = /TODO/g;
|
||
let match;
|
||
|
||
while ((match = regex.exec(text))) {
|
||
const range = new vscode.Range(
|
||
document.positionAt(match.index),
|
||
document.positionAt(match.index + match[0].length)
|
||
);
|
||
|
||
const diagnostic = new vscode.Diagnostic(
|
||
range,
|
||
'TODO found',
|
||
vscode.DiagnosticSeverity.Warning
|
||
);
|
||
|
||
diagnostics.push(diagnostic);
|
||
}
|
||
|
||
diagnosticCollection.set(document.uri, diagnostics);
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 10. 调试和诊断
|
||
|
||
### 10.1 输出通道
|
||
|
||
```typescript
|
||
const outputChannel = vscode.window.createOutputChannel('My Extension');
|
||
context.subscriptions.push(outputChannel);
|
||
|
||
outputChannel.appendLine('Extension activated');
|
||
outputChannel.show(); // 显示输出面板
|
||
```
|
||
|
||
### 10.2 日志记录
|
||
|
||
```typescript
|
||
// 使用 LogOutputChannel(带时间戳)
|
||
const logger = vscode.window.createOutputChannel('My Extension', { log: true });
|
||
|
||
logger.trace('Trace message');
|
||
logger.debug('Debug message');
|
||
logger.info('Info message');
|
||
logger.warn('Warning message');
|
||
logger.error('Error message');
|
||
```
|
||
|
||
### 10.3 错误处理
|
||
|
||
```typescript
|
||
try {
|
||
await riskyOperation();
|
||
} catch (error) {
|
||
if (error instanceof Error) {
|
||
vscode.window.showErrorMessage(`Error: ${error.message}`);
|
||
logger.error(error.stack || error.message);
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 最佳实践总结
|
||
|
||
### ✅ 推荐做法
|
||
|
||
1. **资源管理**:所有 disposable 对象都放入 `context.subscriptions`
|
||
2. **延迟激活**:使用 `onStartupFinished` 而不是 `*`
|
||
3. **异步操作**:使用 `async/await` 处理异步操作
|
||
4. **错误处理**:捕获异常并给用户友好提示
|
||
5. **类型安全**:充分利用 TypeScript 类型系统
|
||
6. **状态持久化**:使用 `globalState`/`workspaceState` 保存状态
|
||
7. **敏感信息**:使用 `secrets` API 存储 Token、密码等
|
||
|
||
### ❌ 避免做法
|
||
|
||
1. 不要在 `activate` 中执行耗时操作
|
||
2. 不要忘记清理资源(监听器、Webview 等)
|
||
3. 不要在 Webview 中直接访问文件系统
|
||
4. 不要在配置中存储敏感信息
|
||
5. 不要阻塞主线程(使用 Worker 或异步操作)
|
||
|
||
---
|
||
|
||
## 参考资源
|
||
|
||
- [VS Code Extension API 官方文档](https://code.visualstudio.com/api)
|
||
- [Extension Samples](https://github.com/microsoft/vscode-extension-samples)
|
||
- [Extension Guidelines](https://code.visualstudio.com/api/references/extension-guidelines)
|