Skip to content

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: 插件激活机制是怎样的?

延迟加载

  1. VSCode 启动时不加载插件代码
  2. 触发 activationEvents 中定义的事件时才加载
  3. 调用 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
  });
}

前端面试知识库