大家都知道,整个VSCode编辑器就是一张大的网页,其实,我们还可以在Visual Studio Code中创建完全自定义的、可以间接和nodejs通信的特殊网页(通过一个acquireVsCodeApi特殊方法),这个网页就叫WebView。内置的Markdown的预览就是使用WebView实现的。使用Webview可以构建复杂的、支持本地文件操作的用户界面。
VSCode插件的WebView类似于iframe的实现,但并不是真正的iframe(我猜底层应该还是基于iframe实现的,只不过上层包装了一层),通过开发者工具可以看到:
demo在我们的vscode-plugin-demo中,我写了一个非常简单、没啥实际意义的Webview示例仅供参考,在任意编辑器右键可以看到打开Webview的菜单:
什么时候适合使用WebView虽然Webview令人很振奋,因为基于它我们可以随意发挥不受限制,但必须注意还是要慎用,毕竟VSCode是很注重性能的,不能因为你一个插件拖累了整个IDE,一般仅在原有API和功能以及交互方式无法满足你时才需要考虑,另外,设计糟糕的Webview也很容易在VS Code中让人感觉不舒适,不能让人家一看就觉得你这是一张网页,好看的UI也很重要。
这是官网给出的建议,在使用webview之前请考虑以下事项:
这个功能真的需要放在VSCode中吗?作为单独的应用程序或网站会不会更好呢?
webview是实现这个功能的唯一方法吗?可以使用常规VS Code API吗?
您的webview是否会带来足够的用户价值以证明其高资源成本?
正式开始WebView之旅 创建WebView context.subscriptions.push(vscode.commands.registerCommand('extension.demo.openWebview', function (uri) { // 创建webview const panel = vscode.window.createWebviewPanel( 'testWebview', // viewType "WebView演示", // 视图标题 vscode.ViewColumn.One, // 显示在编辑器的哪个部位 { enableScripts: true, // 启用JS,默认禁用 retainContextWhenHidden: true, // webview被隐藏时保持状态,避免被重置 } ); panel.webview.html = `<html><body>你好,我是Webview</body></html>`几点说明:
默认情况下,在Web视图中禁用JavaScript,但可以通过传入enableScripts: true选项轻松启用;
默认情况下当webview被隐藏时资源会被销毁,通过retainContextWhenHidden: true会一直保存,但会占用较大内存开销,仅在需要时开启;
加载本地资源出于安全考虑,Webview默认无法直接访问本地资源,它在一个孤立的上下文中运行,想要加载本地图片、js、css等必须通过特殊的vscode-resource:协议,网页里面所有的静态资源都要转换成这种格式,否则无法被正常加载。
vscode-resource:协议类似于file:协议,但它只允许访问特定的本地文件。和file:一样,vscode-resource:从磁盘加载绝对路径的资源。
我简单封装了一个转换方法:
/** * 获取某个扩展文件相对于webview需要的一种特殊路径格式 * 形如:vscode-resource:/Users/toonces/projects/vscode-cat-coding/media/cat.gif * @param context 上下文 * @param relativePath 扩展中某个文件相对于根目录的路径,如 images/test.jpg */ getExtensionFileVscodeResource: function(context, relativePath) { const diskPath = vscode.Uri.file(path.join(context.extensionPath, relativePath)); return diskPath.with({ scheme: 'vscode-resource' }).toString(); }默认情况下,vscode-resource:只能访问以下位置中的资源:
扩展程序安装目录中的文件。
用户当前活动的工作区内。
当然,你还可以使用dataURI直接在Webview中嵌入资源,这种方式没有限制;
从文件加载HTML内容默认不支持从文件加载HTML,需要自己封装代码,我简单封装了一个供大家参考:
/** * 从某个HTML文件读取能被Webview加载的HTML内容 * @param {*} context 上下文 * @param {*} templatePath 相对于插件根目录的html文件相对路径 */ function getWebViewContent(context, templatePath) { const resourcePath = path.join(context.extensionPath, templatePath); const dirPath = path.dirname(resourcePath); let html = fs.readFileSync(resourcePath, 'utf-8'); // vscode不支持直接加载本地资源,需要替换成其专有路径格式,这里只是简单的将样式和JS的路径替换 html = html.replace(/(<link.+?href="|<script.+?src="|<img.+?src=")(.+?)"/g, (m, $1, $2) => { return $1 + vscode.Uri.file(path.resolve(dirPath, $2)).with({ scheme: 'vscode-resource' }).toString() + '"'; }); return html; }运行这段代码之后,会自动将HTML文件中link、href、script、img的资源相对路径全部替换成正确的vscode-resource:绝对路径,例如:
../../lib/vue-2.5.17/vue.js 变成 vscode-resource:/Users/test/workspace/vscode-plugin-demo/lib/vue-2.5.17/vue.js使用方法如下:
panel.webview.html = getWebViewContent(context, 'src/view/test-webview.html'); 消息通信