【VS Code插件开发】自定义侧边栏、视图(六)

前言

在 VS Code 开发工具中,可以在侧边栏中创建一个持久化的自定义视图。这个视图可以随着 VS Code 的不同窗口、标签页之间的切换而保持存在。

侧边栏官方文档讲解

registerWebviewViewProvider

在插件的激活阶段,使用 vscode.window.registerWebviewViewProvider 方法来注册自定义视图的提供者。这个方法接受三个参数,如下:

  • 唯一的视图 ID
  • 用于自定义视图的 HTML 内容、事件处理等
  • 可选的配置选项

当你注册一个 Webview 视图提供者时,需要提供一个回调函数来处理视图的创建和配置。这个回调函数就是 resolveWebviewView。

javascript 复制代码
vscode.window.registerWebviewViewProvider("yourViewId", {
    resolveWebviewView: (webviewView, context) => {
        // 在这里设置自定义视图的 HTML 内容、事件处理等
         webviewView.webview.html = '<h1>Hello from Webview!</h1>';
    }
}, {
 webviewOptions: {
	retainContextWhenHidden: true
  }
});

retainContextWhenHidden 是布尔类型,用于控制当 Webview 隐藏(不可见)时是否保留其上下文。当设置为 true 时,在用户切换到其他面板或关闭 Webview 时,Webview 的状态和内容将保持不变。这可以在某些情况下提供更好的用户体验,因为用户在返回到 Webview 时可以继续之前的操作,而不必从头开始。

自定义视图添加到活动栏

viewsContainers 用于定义自定义视图容器。 activitybar 位于编辑器侧边的垂直工具栏。活动栏提供了快速访问各种功能、面板和操作的图标按钮,使用户能够轻松地切换和执行不同的任务。其中参数如下:

  • id:标识符
  • title:标题
  • icon:图标

views 配置用于定义自定义视图(Views)以及这些视图的属性和行为。这些视图可以包括内嵌的 Webview,以便在插件中显示自定义的 Web 内容、UI 界面等。其中参数解释如下:

  • id:视图的唯一标识符,用于在扩展中引用这个视图。
  • name:视图的名称,将在用户界面中显示。用户将通过这个名称来识别视图。
  • type:视图的类型。在这里,设置为 "webview",表示在视图中使用 Webview 来显示自定义的 Web 内容。

通过使用 viewsContainers"views 配置,可以创建自己的自定义视图和容器,将其添加到活动栏中,并与 Webview 结合以显示自定义的界面和内容。

javascript 复制代码
"viewsContainers": {
  "activitybar": [
    {
      "id": "wxRead-container",
      "title": "wxRead",
      "icon": "media/logo.png"
    }
  ]
},
 "views": {
      "wxRead-container": [
        {
          "id": "wxRead-view",
          "type": "webview",
          "name": "wxRead"
        }
      ]
    }

结果展示如下

案例

我们自定义视图的时,简单的可以直接在registerWebviewViewProvider的第二个参数进行设置,不过我们处理复杂的逻辑的时候,一般会封装一个视图,然后引入到extension.ts中使用。

新建SidebarProvider.ts文件

在构造函数中,传入扩展的根路径 _extensionUri 和扩展的上下文 context。通过 _context.globalState.get('Token') 获取之前存储的 token。

resolveWebviewView 方法中,配置 Webview 的选项,使其支持运行脚本和加载本地资源。

使用 webviewView.webview.onDidReceiveMessage 监听 Webview 内的消息,并根据不同的 data.command 执行不同的操作。在这个示例中,包括处理登录和登出请求。

loginRequest 方法用于发送登录请求,通过 axios 发送 POST 请求到服务器,处理服务器返回的数据。

_getHtmlForWebview 方法用于生成 Webview 的 HTML 内容。它通过 asWebviewUri 方法获取资源文件的 URI,设置 Content Security Policy(CSP),并嵌入需要加载的脚本和样式。

其中某些参数的解释:

  • enableScripts: true: 这个选项设置为 true,允许在 Webview 中运行 JavaScript 脚本。如果你的 Webview 需要执行一些交互式操作或展示动态内容,你需要将这个选项设置为 true。

  • localResourceRoots: [this._extensionUri]: localResourceRoots 是一个数组,指定了可以从本地加载的资源的根路径。在这个代码中,this._extensionUri 是扩展的根路径 URI,这表示 Webview 可以从扩展的根路径加载本地资源。

_getHtmlForWebview方法中:

styleResetUri、styleVSCodeUri、scriptUri 和 styleMainUri:这些是通过webview.asWebviewUri方法获取的资源文件的 URI。这些 URI 是扩展中的 CSS 样式表和 JavaScript 脚本的位置。

nonce:这是一个用于 CSP 的 nonce 值,用于限制只有特定 nonce 值的脚本能够被执行。

<meta http-equiv="Content-Security-Policy">:这是 Content Security Policy(CSP)的设置,用于指定允许加载的资源和脚本。它限制了从 https 或扩展目录加载的图像,只允许特定 nonce 值的脚本被执行。

最后,返回的 HTML 包括引用了所需资源的 <link> 标签和 <script> 标签。其中,<script> 标签引用了你的编译后的 JavaScript 脚本,nonce 值用于安全性,以确保只有符合 nonce 条件的脚本被执行。

javascript 复制代码
import * as vscode from "vscode";
import { getNonce } from "./getNonce";
import axios from "axios"
import { error } from "console";
export class SidebarProvider implements vscode.WebviewViewProvider {

   _view?: vscode.WebviewView;//存储 Webview 视图
  private _context: vscode.ExtensionContext; // 存储扩展上下文对象
  private _token: string | undefined;// 存储token
  
  constructor(private readonly _extensionUri: vscode.Uri, context: vscode.ExtensionContext) {
    this._context = context;
    this._token = this._context.globalState.get('Token');
  }
  public resolveWebviewView(webviewView: vscode.WebviewView) {
    this._view = webviewView;
    webviewView.webview.options = {
      enableScripts: true,
      localResourceRoots: [this._extensionUri],
    };
    webviewView.webview.onDidReceiveMessage(async (data) => {
      console.log(data, data.command, 'command1111111111')
      switch (data.command) {
        case "login": {
          let obj;
          if (data && data.loginname) {
            obj = { username: data.loginname, password: data.password }
          } else {
            obj = { username: this._userInfo?.loginname, password: this._userInfo?.password }
          }
          console.log(this._userInfo, 'this._userInfo')
          this.loginRequest(obj, webviewView);
          break;
        }
        case "logout": {
          axios.post( 
            `/logout`,
            { username: this._userInfo?.username, password: this._userInfo?.password },
            {
              headers: {
                Authorization: `Bearer ${this._token}`, // 假设 token 的类型是 Bearer token
              },
            }
          ).then((res) => {
            console.log(res, "res")
            if (res.data.code === 200) {
              this._isLoggedIn = false;
              webviewView.webview.html = this._getHtmlForWebview(webviewView.webview)//用于生成 Webview 的 HTML 内容
              this._context.globalState.update('Token', '');
              this._token = '';
              vscode.commands.executeCommand('extension.logoutSuccess');
            }
            if (res.data.msg) vscode.window.showInformationMessage(res.data.msg);
          }).catch(error => {
            vscode.window.showErrorMessage("服务器连接错误!");
          })
          break;
        }
      }
    });
  }
  public loginRequest(data: any, webviewView: vscode.WebviewView) {
    axios.post(`/login`, data)
      .then(res => {
        if (res.data.code === 200) {
         
          webviewView.webview.html = this._getHtmlHome(webviewView.webview);//用于生成 Webview 的 HTML 内容
          webviewView.webview.postMessage({
            command: 'loginResponse',
            success: true,
            message: { ...res.data, imageUri, password,loginname: data.username }
          });
          this._token = res.data.token;
          vscode.commands.executeCommand('extension.loginSuccess', res.data);
          vscode.window.showInformationMessage(res.data.msg);
          
        } else {
          vscode.window.showErrorMessage(res.data.msg);
        }
      }).catch(error => {
        vscode.window.showErrorMessage("服务器连接错误!");
      })
  }
  public revive(panel: vscode.WebviewView) {
    this._view = panel;//将传入的 Webview 视图存储到 _view 成员变量中,用于后续操作
  }
  // 在其他地方调用此方法来设置缓存的数据
  public setData(data: any) {
  }
  // 在其他地方调用此方法来获取缓存的数据
  public getData() {
  }
  //用于生成 Webview 的 HTML 内容
  private _getHtmlForWebview(webview: vscode.Webview) {
    //asWebviewUri获取资源文件的 URI
    const styleResetUri = webview.asWebviewUri(
      vscode.Uri.joinPath(this._extensionUri, "media", "reset.css")
    );
    const styleVSCodeUri = webview.asWebviewUri(
      vscode.Uri.joinPath(this._extensionUri, "media", "vscode.css")
    );
    const scriptUri = webview.asWebviewUri(
      vscode.Uri.joinPath(this._extensionUri, "out", "compiled/HelloWorld.js")
    );
    const styleMainUri = webview.asWebviewUri(
      vscode.Uri.joinPath(this._extensionUri, "out", "compiled/HelloWorld.css")
    );
    // Use a nonce to only allow a specific script to be run.
    const nonce = getNonce();
    let a = 0;
    a++;
    return `<!DOCTYPE html>
			<html lang="en">
			<head>
				<meta charset="UTF-8">
				<!--
					Use a content security policy to only allow loading images from https or from our extension directory,
					and only allow scripts that have a specific nonce.
        -->
        <meta http-equiv="Content-Security-Policy" content=" img-src https: data:; style-src 'unsafe-inline' ${webview.cspSource
      }; script-src 'nonce-${nonce}';">
				<meta name="viewport" content="width=device-width, initial-scale=1.0">
				<link href="${styleResetUri}" rel="stylesheet">
				<link href="${styleVSCodeUri}" rel="stylesheet">
        <link href="${styleMainUri}" rel="stylesheet">
			</head>
      <body>
				<script nonce="${nonce}" src="${scriptUri}"></script>
			</body> 
			</html>`;
  }
}

extension.ts中引入

import SidebarProvider from './SidebarProvider':这里导入自定义 SidebarProvider 类,用于管理 Webview 视图的创建和交互。

创建 readerViewProvider 实例:通过 new SidebarProvider(context.extensionUri, context) 创建了一个 SidebarProvider 实例,将扩展的根路径和上下文对象传递给它。

使用 vscode.window.registerWebviewViewProvider 注册 Webview 视图提供者:这行代码注册了你的 readerViewProvider 实例作为 'wxRead-view' 标识符的 Webview 视图提供者。同时,通过传递一个配置对象,你设置了 Webview 的选项,其中 retainContextWhenHidden 设置为 true,以便在 Webview 隐藏时保留其上下文。

其中部分参数解释如下:

  • context :用于传递上下文信息和提供功能的对象。

  • context.extensionUri:用于获取当前扩展的根路径的 Uniform Resource Identifier (URI)。这个 URI 表示扩展在文件系统中的位置,可以用于引用扩展中的资源文件、图标、样式表等。具体如下:

javascript 复制代码
    import SidebarProvider from './SidebarProvider';
	const readerViewProvider =new SidebarProvider(context.extensionUri, context)
	vscode.window.registerWebviewViewProvider('wxRead-view', readerViewProvider, {
		webviewOptions: {
			retainContextWhenHidden: true,
		},
	});
相关推荐
mango大侠4 天前
Ubuntu24.04 安装 visual studio code
ide·vscode·编辑器·visual studio code
故苏呦4 天前
Visual Studio Code 快捷键
visual studio code·visual studio
White graces7 天前
掌握HTML, 从零开始的网页设计
开发语言·前端·windows·edge·html·visual studio code
码农老起7 天前
常用代码开发工具技术分享
git·docker·github·visual studio code·visual studio
草梅友仁11 天前
2024 年第 51 周草梅周报:Windsurf,比 Cursor 更好用的 AI 编辑器
aigc·visual studio code·bun
子洋20 天前
Mac 下 vscode 更新报错
前端·javascript·visual studio code
Eric_见嘉24 天前
Cursor 会被打败:使用 Windsurf 一键生成 2048 小游戏
前端·aigc·visual studio code
巫师练法术25 天前
VScode下构建python的虚拟环境
visual studio code
Yang.991 个月前
基于Windows系统用C++做一个点名工具
c++·windows·sql·visual studio code·sqlite3
WXDWIN.1 个月前
C++语言之模版与类型转换
c语言·开发语言·c++·visualstudio·visual studio code