配置变更监听
用户修改配置后,插件需要感知变化并及时响应。onDidChangeConfiguration 是配置驱动的核心事件。
监听配置变更
typescript
import * as vscode from 'vscode';
export function activate(context: vscode.ExtensionContext) {
// 监听所有配置变更
context.subscriptions.push(
vscode.workspace.onDidChangeConfiguration((e) => {
console.log('配置发生了变化');
})
);
}事件对象 e 携带变更信息,最常用的是 affectsConfiguration。
affectsConfiguration 检查影响
e.affectsConfiguration(section) 判断变更是否涉及指定配置:
typescript
vscode.workspace.onDidChangeConfiguration((e) => {
// 检查是否影响了 mylinter 前缀下的配置
if (e.affectsConfiguration('mylinter')) {
refreshLinter();
}
// 检查是否影响了具体配置项
if (e.affectsConfiguration('mylinter.enable')) {
toggleLinter();
}
// 检查嵌套配置
if (e.affectsConfiguration('mylinter.rules.no-console')) {
updateRules();
}
});检查粒度对比
| 写法 | 触发条件 |
|---|---|
e.affectsConfiguration('mylinter') | 任何 mylinter.* 配置变化 |
e.affectsConfiguration('mylinter.enable') | 仅 enable 变化 |
e.affectsConfiguration('mylinter.rules') | rules 及其子项变化 |
用前缀形式监听整段配置最常用,能覆盖所有相关变更。
变更响应策略
配置变更后的响应分为三类,按开销递增:
1. 直接生效(无需重载)
适合大多数配置:读取新值并更新插件状态。
typescript
class LinterController {
private enabled = true;
private rules: Record<string, string> = {};
constructor(context: vscode.ExtensionContext) {
context.subscriptions.push(
vscode.workspace.onDidChangeConfiguration((e) => {
if (!e.affectsConfiguration('mylinter')) return;
this.applyConfig(); // 读取新配置并应用
})
);
this.applyConfig();
}
private applyConfig() {
const config = vscode.workspace.getConfiguration('mylinter');
this.enabled = config.get<boolean>('enable', true);
this.rules = config.get('rules', {});
// 触发诊断刷新
this.diagnosticCollection.clear();
this.scanAndReport();
}
}2. 需要重启
某些配置影响扩展生命周期(如语言服务器路径),提示用户重启:
typescript
vscode.workspace.onDidChangeConfiguration((e) => {
if (e.affectsConfiguration('mylinter.executablePath')) {
vscode.window
.showInformationMessage(
'检查器路径已修改,重启后生效',
'立即重启'
)
.then((choice) => {
if (choice === '立即重启') {
vscode.commands.executeCommand('workbench.action.reloadWindow');
}
});
}
});3. 需要重载窗口
影响 VS Code 进程级配置时提示重载:
typescript
vscode.workspace.onDidChangeConfiguration((e) => {
if (e.affectsConfiguration('mylinter.useWorker')) {
const action = await vscode.window.showWarningMessage(
'工作线程模式已切换,需要重载窗口生效',
'重载窗口'
);
if (action === '重载窗口') {
await vscode.commands.executeCommand('workbench.action.reloadWindow');
}
}
});响应策略选择
| 变更内容 | 策略 |
|---|---|
| 规则开关/级别 | 直接生效 |
| 超时/缓存大小 | 直接生效 |
| 可执行文件路径 | 重启扩展 |
| 进程级开关 | 重载窗口 |
| 语言服务器配置 | 重启 |
配置与激活事件联动
配置变更可以触发激活逻辑,与 activationEvents 协同:
activationEvents.onStartupFinished
json
{
"activationEvents": ["onStartupFinished"]
}插件启动时读取配置初始化,后续通过监听响应变更:
typescript
export function activate(context: vscode.ExtensionContext) {
const controller = new LinterController(context);
// 启动时若关闭则跳过初始化
if (!controller.enabled) {
return; // 等待启用配置后再激活
}
}按需激活与配置监听
typescript
// 配置变更可能激活插件(onCommand 场景)
// 插件被命令激活后,仍需监听配置以同步状态
export function activate(context: vscode.ExtensionContext) {
// 若插件因命令激活,先读取当前配置
const config = vscode.workspace.getConfiguration('mylinter');
const enabled = config.get<boolean>('enable', true);
// 然后监听后续变更
context.subscriptions.push(
vscode.workspace.onDidChangeConfiguration((e) => {
if (e.affectsConfiguration('mylinter.enable')) {
vscode.commands.executeCommand('setContext', 'mylinter.enabled',
vscode.workspace.getConfiguration('mylinter').get('enable'));
}
})
);
}配合 setContext 更新菜单
配置变更通过 setContext 同步 UI 状态:
typescript
import * as vscode from 'vscode';
export function activate(context: vscode.ExtensionContext) {
const syncContext = () => {
const config = vscode.workspace.getConfiguration('mylinter');
vscode.commands.executeCommand('setContext', 'mylinter.enabled',
config.get<boolean>('enable', true));
};
// 初始同步
syncContext();
// 配置变更时同步
context.subscriptions.push(
vscode.workspace.onDidChangeConfiguration((e) => {
if (e.affectsConfiguration('mylinter.enable')) {
syncContext();
}
})
);
}json
{
"contributes": {
"menus": {
"commandPalette": [
{
"command": "mylinter.run",
"when": "mylinter.enabled"
}
]
}
}
}配置缓存策略
高频读取配置会影响性能,配合监听做缓存:
typescript
class ConfigCache {
private cache = new Map<string, any>();
private config = vscode.workspace.getConfiguration('mylinter');
constructor(context: vscode.ExtensionContext) {
// 变更时清空缓存
context.subscriptions.push(
vscode.workspace.onDidChangeConfiguration((e) => {
if (e.affectsConfiguration('mylinter')) {
this.cache.clear();
this.config = vscode.workspace.getConfiguration('mylinter');
}
})
);
}
get<T>(key: string, fallback: T): T {
if (!this.cache.has(key)) {
this.cache.set(key, this.config.get<T>(key, fallback));
}
return this.cache.get(key) as T;
}
}防抖与批量变更
一次保存 settings.json 可能触发多次变更,用防抖合并处理:
typescript
let timer: NodeJS.Timeout | undefined;
vscode.workspace.onDidChangeConfiguration((e) => {
if (!e.affectsConfiguration('mylinter')) return;
// 防抖:300ms 内多次变更只响应一次
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
applyAllConfig();
}, 300);
});完整示例:配置驱动的实时响应
typescript
import * as vscode from 'vscode';
export function activate(context: vscode.ExtensionContext) {
const diagnostics = vscode.languages.createDiagnosticCollection('mylinter');
const applyConfig = () => {
const config = vscode.workspace.getConfiguration('mylinter');
const enabled = config.get<boolean>('enable', true);
if (!enabled) {
diagnostics.clear();
return;
}
// 读取规则并重新检查
const rules = config.get<Record<string, string>>('rules', {});
runChecks(diagnostics, rules);
};
// 初始应用
applyConfig();
// 变更监听:规则变化实时重新检查
context.subscriptions.push(
vscode.workspace.onDidChangeConfiguration((e) => {
if (e.affectsConfiguration('mylinter')) {
applyConfig();
}
})
);
// 清理
context.subscriptions.push(diagnostics);
}配置监听让插件对用户设置"实时响应"。下一篇转向国际化:把插件翻译成多种语言。