激活函数与生命周期
每个插件都有完整的生命周期:激活(activate)→ 运行 → 停用(deactivate)。理解生命周期,才能把注册与清理放在正确的位置,避免资源泄漏。
生命周期总览
VS Code 启动/事件触发
│
▼
Extension Host
│
加载插件模块
│
▼
activate(context) ← 插件激活入口
│
注册命令/监听器
│
运行期间执行逻辑
│
▼
deactivate() ← 插件停用(VS Code 关闭/插件被禁用)activate 函数
插件入口文件(main 指定的 JS 文件)必须导出 activate:
typescript
export function activate(context: vscode.ExtensionContext) {
// 插件被激活时执行
}注册时机
activate 在以下时机被调用:
| 触发场景 | 说明 |
|---|---|
| 首次执行插件命令 | 最常见 |
| 打开匹配语言的文件 | onLanguage 激活 |
| 视图变为可见 | onView 激活 |
| 启动完成 | onStartupFinished 激活 |
显式 * | 启动即激活 |
在 activate 中完成:
- 注册命令(
registerCommand) - 注册监听器(
onDidChange*) - 注册语言服务(Provider)
- 创建视图、状态栏等 UI
返回值
activate 可以返回一个 API 对象,供其他插件通过 getExtension 获取:
typescript
export function activate(context: vscode.ExtensionContext) {
return {
hello() {
return 'world';
}
};
}不返回则其他插件只能通过 exports 访问。
deactivate 函数
插件停用时调用,用于释放无法自动清理的资源:
typescript
export function deactivate() {
// 关闭进程、断开连接、清理定时器等
}大多数资源不需要手动清理——通过 context.subscriptions 注册的 Disposable 会被自动回收。需要手动处理的是:
- 启动的子进程
- 定时器(
setInterval) - 外部连接(数据库、WebSocket)
懒加载激活策略
VS Code 按需激活插件,这是保证编辑器启动速度的关键:
传统模式:启动时加载全部插件 → 启动慢
懒加载:仅当需要时激活 → 启动快最佳实践
| 策略 | 做法 |
|---|---|
| 最小激活事件 | 只声明必须的 onCommand / onLanguage |
避免 * | 不要无脑启动即激活 |
| 贡献点自动激活 | 1.74+ 可省略大部分 activationEvents |
| 延迟初始化 | 重操作放到首次使用时执行 |
ExtensionContext 与 subscriptions
ExtensionContext
activate(context) 接收的 context 是扩展上下文,提供扩展的生命周期信息:
| 属性/方法 | 用途 |
|---|---|
subscriptions | Disposable 列表,停用时自动清理 |
extensionUri | 扩展安装目录 URI |
extensionPath | 扩展安装目录路径 |
globalState | 全局持久化状态(跨工作区) |
workspaceState | 工作区级持久化状态 |
globalStorageUri | 全局存储目录 |
workspaceStorageUri | 工作区存储目录 |
secrets | 敏感信息安全存储 |
extensionMode | 扩展运行模式(生产/开发/测试) |
subscriptions 管理
所有注册产生的 Disposable 都应加入 subscriptions:
typescript
export function activate(context: vscode.ExtensionContext) {
// 注册命令,返回 Disposable
const command = vscode.commands.registerCommand('myExt.hello', () => {
vscode.window.showInformationMessage('Hello');
});
// 加入 subscriptions,插件停用时自动 dispose
context.subscriptions.push(command);
// 也支持事件监听
const listener = vscode.workspace.onDidChangeConfiguration(() => {
// ...
});
context.subscriptions.push(listener);
}不加入 subscriptions 的后果:插件停用后命令仍被注册、监听器仍工作,产生内存泄漏与重复注册。
Disposable 模式
几乎所有 register* 与 create* API 都返回 Disposable:
| API | 返回 |
|---|---|
registerCommand | Disposable |
registerTreeDataProvider | Disposable |
createStatusBarItem | StatusBarItem(含 dispose) |
createWebviewPanel | WebviewPanel(含 dispose) |
onDidChange* 事件 | Disposable |
统一用 context.subscriptions.push() 管理即可。
生命周期常见陷阱
| 陷阱 | 表现 | 解决 |
|---|---|---|
| 忘 push subscriptions | 插件重载后命令重复 | 全部注册都 push |
| 启动即重初始化 | 每次激活都重建数据 | 判断已存在则复用 |
| 异步激活未完成 | 命令执行时数据未就绪 | 用 async/await 保证顺序 |
| deactivate 空实现 | 子进程残留 | 手动清理外部资源 |
完整示例
typescript
import * as vscode from 'vscode';
export function activate(context: vscode.ExtensionContext) {
console.log('插件已激活');
// 注册命令
context.subscriptions.push(
vscode.commands.registerCommand('myExt.hello', () => {
vscode.window.showInformationMessage('Hello from extension');
})
);
// 状态栏
const statusBar = vscode.window.createStatusBarItem(
vscode.StatusBarAlignment.Right,
100
);
statusBar.text = '$(check) Ready';
statusBar.show();
context.subscriptions.push(statusBar);
}
export function deactivate() {
console.log('插件已停用');
}掌握激活与生命周期管理,插件就能「按需启动、干净退出」,为后续所有功能开发打下基础。