VS Code 插件 Webview 热更新配置
当前位置:点晴教程→知识管理交流
→『 技术文档交流 』
做过 VS Code 插件开发的同学应该都有这个体会:每次改完 Webview 的代码,都得手动刷新才能看到效果,有时候甚至要重启整个插件。 最近在做项目的时候,也是深感没有热更新的痛苦,所以查了一些资料,解决了这个问题,下面分享一下解决过程,希望对你有用: 问题在哪先说说为什么 Webview 不能像普通 Web 项目那样用 HMR。VS Code 的 Webview 本质上是一个受限的 iframe 环境。它的 HTML 是通过字符串注入的,不是从服务器加载的。再加上 CSP 安全策略的限制,传统的热更新方案根本用不了。 没有热更新的情况下,开发流程大概是这样的:
如果是频繁调试 UI 样式,这个流程能把人逼疯。 思路既然没法用传统方案,那就换个思路:监听编译产物的变化,变了就重新设置一遍 具体来说:
具体实现构建配置首先确保构建工具支持 watch 模式。我用的是 esbuild,配置大概是这样: // build.js const esbuild = require("esbuild"); const isWatch = process.argv.includes('--watch'); async function build() { const ctx = await esbuild.context({ entryPoints: ['src/webview/index.tsx'], bundle: true, outfile: 'dist/webview.js', platform: 'browser', loader: { '.tsx': 'tsx', '.css': 'css', }, }); if (isWatch) { await ctx.watch(); console.log('Watching...'); } else { await ctx.rebuild(); await ctx.dispose(); } } build(); package.json 里加上对应的脚本:
Provider 改造重点在 WebviewViewProvider 里面。这里给一个简化版的实现:
import * as vscode from 'vscode'; export class MyWebviewProvider implements vscode.WebviewViewProvider { private view?: vscode.WebviewView; private watcher?: vscode.FileSystemWatcher; constructor( private extensionUri: vscode.Uri, private context: vscode.ExtensionContext ) { this.setupHotReload(); } resolveWebviewView(webviewView: vscode.WebviewView) { this.view = webviewView;
webviewView.webview.options = { enableScripts: true, localResourceRoots: [this.extensionUri] };
webviewView.webview.html = this.getHtml(webviewView.webview); } // 热更新的核心逻辑 private setupHotReload() { // 只在开发模式下启用 if (this.context.extensionMode !== vscode.ExtensionMode.Development) { return; } const pattern = new vscode.RelativePattern( this.extensionUri, 'dist/webview.{js,css}' );
this.watcher = vscode.workspace.createFileSystemWatcher(pattern); // 防抖处理 let timer: NodeJS.Timeout; const reload = () => { clearTimeout(timer); timer = setTimeout(() => { if (this.view) { this.view.webview.html = this.getHtml(this.view.webview); } }, 300); }; this.watcher.onDidChange(reload); this.context.subscriptions.push(this.watcher); } private getHtml(webview: vscode.Webview): string { const scriptUri = webview.asWebviewUri( vscode.Uri.joinPath(this.extensionUri, 'dist', 'webview.js') );
const nonce = this.getNonce(); return `<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'nonce-${nonce}';"> </head> <body> <div id="root"></div> <script nonce="${nonce}" src="${scriptUri}"></script> </body> </html>`; } private getNonce(): string { const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; let result = ''; for (let i = 0; i < 32; i++) { result += chars.charAt(Math.floor(Math.random() * chars.length)); } return result; } } 代码不复杂,关键点有几个: 开发模式判断
文件监听
防抖 esbuild 编译时可能会触发多次文件变化事件,加个 300ms 的防抖可以避免频繁刷新。 重新生成 nonce 每次刷新都要生成新的 nonce 值,不然 CSP 会阻止脚本执行。
|
关键字查询
相关文章
正在查询... |