订阅列表为什么要收集可释放对象,漏了会怎样?
结论
context.subscriptions 是插件的释放清单。推进去的对象实现了 Disposable,插件停用时宿主会逐个调用 dispose。漏掉的监听、定时器、诊断集合、Webview 会留在扩展宿主里,重载后还可能叠成多份。
展开
注册类 API 几乎都返回 Disposable:
context.subscriptions.push(
vscode.commands.registerCommand("myext.hello", () => {
/* ... */
}),
vscode.workspace.onDidChangeTextDocument(onChange),
diagnosticCollection,
statusBarItem
);
停用时宿主遍历这个数组。命令被卸掉,事件不再回调,诊断波浪线被清掉,状态栏项消失。
自己创建的资源也要包一层:
const timer = setInterval(poll, 5000);
context.subscriptions.push({ dispose: () => clearInterval(timer) });
漏收集的典型后果:
onDidChangeTextDocument每重载一次多挂一个监听,补全和诊断被重复计算。createDiagnosticCollection没释放,旧诊断清不掉。setInterval继续打请求。- Webview 面板关不干净,消息回调打到已销毁对象上。
deactivate 里再手动 dispose 一遍通常没必要,订阅列表已经做了。deactivate 留给那些没有放进订阅、又必须按顺序停下的进程,比如语言服务器要先发 shutdown。
容易讲错的点
subscriptions不是响应式状态库,不要拿它存业务数据。- 只
push还不够:如果回调里又createWebviewPanel,新面板也要在关闭时释放,或自己放进订阅。 - 事件回调抛错不会自动取消订阅,宿主仍认为插件活着。