什么是 VSCode Webview 在VSCode中,仅靠原生插件API,无法做到自定义交互页面,这时需要 Webview 来嵌入自定义 Web 应用程序。使用Webview可以构建复杂的、支持各种操作的用户界面。Webview就像一个 iframe 一样,无法与外界(VSCode主界面)直接 交互,需要使用acquireVsCodeApi
特殊方法,这不是本文讨论的内容,不再深入。
显示一个 Webview 界面有两种方法,这篇文章主要说明第二个。
直接创建一个 Webview 标签页
将 Webview 添加进视图容器
在使用Webview前,你需要考虑几个问题:
你的webview是否会带来足够的用户价值,以作为提高资源占用 的交换? 你的webview是否设计合理、美观? 这个功能必须放在VSCode中吗,能否作为单独的网页?
构建 Webview Provider 顾名思义,Webview提供者,是提供webview视图的类,为谁提供?当然是视图容器(viewcontainer),视图放在视图容器内 。
这个类继承自接口WebviewViewProvider
,该接口只有一个定义resolveWebviewView
。在这个类中,还额外保存了extensionUri,以供后续定位文件。
主逻辑 "src/panels/someWebviewProvider.ts" 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import { CancellationToken , WebviewView , WebviewViewProvider , WebviewViewResolveContext , Webview , Uri } from "vscode" ;export class SomeWebviewProvider implements WebviewViewProvider { public constructor ( private readonly _extensionUri: Uri, ) { } resolveWebviewView ( webviewView: WebviewView, context: WebviewViewResolveContext, token: CancellationToken ) { throw new Error ("Not Implemented" ); } }
Webview 的独特标识符 另外,建议添加一个字段来表明此 Webview 的独特类型。这个类型在最后一步 添加插件贡献 处会用到。
1 public static readonly viewType = 'example-extension.webview.test' ;
resolveWebviewView 这个方法的作用是,在视图(View)可见前解析Webview内容,这个webviewView
参数就是视图。
至于解析,就是放进去HTML啦 ,直接为webviewView.webview.html
赋值!
在这一段代码中,还可以
使用onDidReceiveMessage
和vscode通信
更改webview的设置
"src/panels/someWebviewProvider.ts" 1 2 3 4 5 6 7 8 9 10 webviewView.webview .options = { enableScripts : true , localResourceRoots : [ this ._extensionUri , ] }; webviewView.webview .html = this ._getHtmlForWebview (webviewView.webview );
静态资源 出于安全考虑,Webview不能直接访问本地资源,想要加载静态资源,必须通过特殊的vscode-resource:
协议 ,网页里面所有的静态资源都要转换成这种格式,否则无法被正常加载。我推荐封装一个工具函数:
"src/utilities/getUri.ts" 1 2 3 4 5 import { Uri , Webview } from "vscode" ;export function getUri (webview: Webview, extensionUri: Uri, pathList: string [] ) { return webview.asWebviewUri (Uri .joinPath (extensionUri, ...pathList)); }
我们使用 getUri(webview, this._extensionUri, ["out", "webview.js"])
获取到了项目目录 中的/out/webview.js
,这是webview的具体逻辑。这 js 通常由 ts 编译而来,以 esbuild 为例:
"esbuild.js" 1 2 3 4 5 6 7 8 9 const webviewConfig = { bundle : true , minify : process.env .NODE_ENV === "production" , sourcemap : process.env .NODE_ENV !== "production" , target : "es2020" , format : "esm" , entryPoints : ["./src/webview/main.ts" ], outfile : "./out/webview.js" , };
_getHtmlForWebview 解析资源路径,然后返回字符串HTML。getUri
和getNonce
为工具函数,下文有提及。
"src/panels/someWebviewProvider.ts" 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 private _getHtmlForWebview (webview: Webview ) { const webviewUri = getUri (webview, this ._extensionUri , ["out" , "webview.js" ]); const cssUri = getUri (webview, this ._extensionUri , ["out" , "style.css" ]); const nonce = getNonce (); return ` <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Hello World!</title> <link rel="stylesheet" href="${cssUri} "> </head> <body> <script type="module" nonce="${nonce} " src="${webviewUri} "></script> </body> </html> ` ;}
一些无关痛痒的工具函数,上文用到 "src/utilities/getNonce.ts" 1 2 3 4 5 6 7 8 export function getNonce ( ) { let text = "" ; const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" ; for (let i = 0 ; i < 32 ; i++) { text += possible.charAt (Math .floor (Math .random () * possible.length )); } return text; }
注册 在插件的激活方法中注册 Provider:
"src/extension.js" 1 2 3 4 5 6 7 8 9 10 export function activate (context: ExtensionContext ) { const webview = window .registerWebviewViewProvider ( SomeWebviewProvider .viewType , new SomeWebviewProvider (context.extensionUri ) ); context.subscriptions .push (webview); }
定义 Contributes 插件贡献 与其翻译成插件贡献,不如说是插件的功能,注册了哪些命令啊,提供了什么视图……
在pakage.json
中指定插件的“贡献”:
viewsContainers
视图容器,可以被放置在侧边栏,就像“资源管理器”、“搜索”和“插件”一样。
views
视图,为上面定义的视图容器,添加视图,就像“资源管理器”中的“大纲”、“时间线”等小栏目。
"package.json" 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 "contributes" : { "viewsContainers" : { "activitybar" : [ { "id" : "example-extension-container" , "title" : "Example" , "icon" : "./assets/extension-icon.png" } ] } , "views" : { "example-extension-container" : [ { "type" : "webview" , "id" : "example-extension.webview.test" , "name" : "View 1" , "icon" : "./assets/extension-icon.png" } ] } } ,