网页视图销毁和重复创建会出什么问题?
结论
面板关掉后还往它 postMessage,会抛「网页已被释放」。每次命令都 createWebviewPanel,会开出一排重复标签,监听器也会叠。正确做法是复用已有面板,在 onDidDispose 里清掉引用和订阅。
展开
let panel: vscode.WebviewPanel | undefined;
function open(context: vscode.ExtensionContext) {
if (panel) {
panel.reveal(vscode.ViewColumn.Beside);
return;
}
panel = vscode.window.createWebviewPanel("myext.preview", "预览", vscode.ViewColumn.Beside, {
enableScripts: true
});
const listener = panel.webview.onDidReceiveMessage(handle);
panel.onDidDispose(() => {
listener.dispose();
panel = undefined;
});
}
重复创建的后果:
- 用户不知道哪一个是最新的
- 每份页面都在请求同一份数据
- 旧页面的消息回调还在改当前文档
销毁后没清引用的后果:
- 异步请求回来继续
panel.webview.postMessage - 编辑器抛错,或消息发进一个已经关闭的会话
- 定时器继续跑
dispose 可以由用户关标签触发,也可以由插件主动调用。主动调用后同样会进 onDidDispose,清引用的逻辑只写在这一处。
开发时热重载如果没释放,旧面板的回调和新代码并存。注册都放进 context.subscriptions,或在上面的释放回调里成对解开。
容易讲错的点
reveal不会重新执行resolve。已有面板的数据更新要发消息,不要假设它会再读一遍初始 HTML。- 侧边栏视图不是你创建的,不要对它调用面板那套
createWebviewPanel。视图由用户展开,你只在resolveWebviewView里接。 - 监听
onDidReceiveMessage的释放对象如果没处理,面板关了回调还在。