VSCode 插件核心架构
激活机制、命令系统与配置管理
目录
插件结构
项目文件
my-extension/
├── package.json # 插件清单
├── src/
│ ├── extension.ts # 入口文件
│ └── commands/
│ └── index.ts
├── tsconfig.json
└── .vscode/
└── launch.json # 调试配置package.json
json
{
"name": "my-extension",
"displayName": "My Extension",
"description": "A sample extension",
"version": "0.0.1",
"engines": {
"vscode": "^1.80.0"
},
"categories": ["Other"],
"activationEvents": [
"onLanguage:javascript",
"onCommand:myExtension.helloWorld"
],
"main": "./dist/extension.js",
"contributes": {
"commands": [
{
"command": "myExtension.helloWorld",
"title": "Hello World",
"category": "My Extension"
}
],
"keybindings": [
{
"command": "myExtension.helloWorld",
"key": "ctrl+shift+h",
"mac": "cmd+shift+h"
}
],
"configuration": {
"title": "My Extension",
"properties": {
"myExtension.enable": {
"type": "boolean",
"default": true,
"description": "Enable the extension"
}
}
},
"menus": {
"editor/context": [
{
"command": "myExtension.helloWorld",
"when": "editorHasSelection",
"group": "navigation"
}
]
}
},
"scripts": {
"vscode:prepublish": "npm run compile",
"compile": "tsc -p ./",
"watch": "tsc -watch -p ./"
},
"devDependencies": {
"@types/vscode": "^1.80.0",
"@types/node": "^18.0.0",
"typescript": "^5.0.0"
}
}激活机制
激活事件
json
{
"activationEvents": [
// 文件类型
"onLanguage:javascript",
"onLanguage:typescript",
// 命令
"onCommand:myExtension.start",
// 文件系统
"onFileSystem:sftp",
"workspaceContains:**/.myconfig",
// 视图
"onView:myTreeView",
// URI
"onUri",
// 立即激活(不推荐)
"*"
]
}入口函数
typescript
// extension.ts
import * as vscode from 'vscode';
// 插件激活时调用
export function activate(context: vscode.ExtensionContext) {
console.log('Extension activated!');
// 注册命令
const disposable = vscode.commands.registerCommand(
'myExtension.helloWorld',
() => {
vscode.window.showInformationMessage('Hello World!');
}
);
// 添加到订阅列表(自动清理)
context.subscriptions.push(disposable);
}
// 插件停用时调用
export function deactivate() {
console.log('Extension deactivated');
}ExtensionContext
typescript
interface ExtensionContext {
// 订阅管理
subscriptions: Disposable[];
// 持久化存储
globalState: Memento; // 全局状态
workspaceState: Memento; // 工作区状态
secrets: SecretStorage; // 敏感数据
// 路径
extensionPath: string; // 插件安装路径
extensionUri: Uri;
globalStoragePath: string; // 全局存储路径
storagePath?: string; // 工作区存储路径
// 环境
extensionMode: ExtensionMode; // Development / Production / Test
}命令系统
注册命令
typescript
// 基础命令
vscode.commands.registerCommand('myExtension.sayHello', () => {
vscode.window.showInformationMessage('Hello!');
});
// 带参数命令
vscode.commands.registerCommand('myExtension.openFile', (uri: vscode.Uri) => {
vscode.window.showTextDocument(uri);
});
// 编辑器命令(可访问编辑器实例)
vscode.commands.registerTextEditorCommand(
'myExtension.insertDate',
(textEditor, edit) => {
const position = textEditor.selection.active;
edit.insert(position, new Date().toISOString());
}
);执行命令
typescript
// 执行自定义命令
await vscode.commands.executeCommand('myExtension.sayHello');
// 执行内置命令
await vscode.commands.executeCommand('vscode.open', vscode.Uri.file('/path/to/file'));
// 带参数
await vscode.commands.executeCommand('editor.action.formatDocument');命令面板集成
json
{
"contributes": {
"commands": [
{
"command": "myExtension.format",
"title": "Format Document",
"category": "My Extension",
"icon": "$(edit)"
}
],
"menus": {
"commandPalette": [
{
"command": "myExtension.format",
"when": "editorLangId == javascript"
}
]
}
}
}配置项
定义配置
json
{
"contributes": {
"configuration": {
"title": "My Extension",
"properties": {
"myExtension.enable": {
"type": "boolean",
"default": true,
"description": "Enable the extension"
},
"myExtension.maxItems": {
"type": "number",
"default": 100,
"minimum": 1,
"maximum": 1000
},
"myExtension.format": {
"type": "string",
"enum": ["compact", "pretty"],
"default": "pretty",
"enumDescriptions": [
"Compact output",
"Pretty printed output"
]
},
"myExtension.ignore": {
"type": "array",
"items": { "type": "string" },
"default": ["node_modules"]
}
}
}
}
}读取配置
typescript
// 获取配置对象
const config = vscode.workspace.getConfiguration('myExtension');
// 读取值
const enabled = config.get<boolean>('enable', true);
const maxItems = config.get<number>('maxItems', 100);
// 更新配置
await config.update('enable', false, vscode.ConfigurationTarget.Global);
await config.update('enable', true, vscode.ConfigurationTarget.Workspace);
// 监听配置变化
vscode.workspace.onDidChangeConfiguration((e) => {
if (e.affectsConfiguration('myExtension.enable')) {
const newValue = config.get<boolean>('enable');
console.log('enable changed to:', newValue);
}
});状态管理
持久化存储
typescript
// 全局状态(跨工作区)
context.globalState.update('lastOpened', Date.now());
const lastOpened = context.globalState.get<number>('lastOpened');
// 工作区状态
context.workspaceState.update('projectConfig', { version: 1 });
const projectConfig = context.workspaceState.get('projectConfig');
// 密钥存储(加密)
await context.secrets.store('apiKey', 'secret-key');
const apiKey = await context.secrets.get('apiKey');
await context.secrets.delete('apiKey');状态栏
typescript
// 创建状态栏项
const statusBarItem = vscode.window.createStatusBarItem(
vscode.StatusBarAlignment.Right,
100
);
statusBarItem.text = '$(sync~spin) Loading...';
statusBarItem.tooltip = 'Click to refresh';
statusBarItem.command = 'myExtension.refresh';
statusBarItem.show();
// 更新
statusBarItem.text = '$(check) Ready';
statusBarItem.backgroundColor = undefined;
// 警告样式
statusBarItem.backgroundColor = new vscode.ThemeColor(
'statusBarItem.warningBackground'
);
context.subscriptions.push(statusBarItem);高频面试题
Q1: 插件激活机制是怎样的?
延迟加载:
- VSCode 启动时不加载插件代码
- 触发
activationEvents中定义的事件时才加载 - 调用
activate()函数完成初始化
Q2: contributes 有哪些常用字段?
| 字段 | 作用 |
|---|---|
commands | 注册命令 |
keybindings | 快捷键绑定 |
configuration | 配置项定义 |
menus | 菜单项 |
views | 侧边栏视图 |
viewsContainers | 视图容器 |
languages | 语言定义 |
Q3: 如何确保插件资源正确清理?
typescript
export function activate(context: vscode.ExtensionContext) {
// 将所有 Disposable 添加到 subscriptions
context.subscriptions.push(
vscode.commands.registerCommand(...),
vscode.workspace.onDidChangeConfiguration(...),
statusBarItem
);
}
export function deactivate() {
// subscriptions 中的资源会自动清理
// 这里处理其他清理逻辑
}Q4: 如何获取当前编辑器信息?
typescript
const editor = vscode.window.activeTextEditor;
if (editor) {
const document = editor.document;
const selection = editor.selection;
console.log({
fileName: document.fileName,
languageId: document.languageId,
lineCount: document.lineCount,
selectedText: document.getText(selection),
cursorPosition: selection.active
});
}