Skip to content
微信公众号

webview视图

Webview API 允许扩展在 Visual Studio Code 中创建完全可自定义的视图。例如,内置的 Markdown 扩展使用 webview 来渲染 Markdown 预览。Webview 还可以用于构建超出 VS Code 原生 API 支持范围的复杂用户界面。

将 webview 视为iframe您的扩展控制的 VS Code 内的内容。Webview 可以在此框架中呈现几乎任何 HTML 内容,并且它使用消息传递与扩展进行通信。这种自由使得网络视图变得异常强大,并开辟了一系列全新的扩展可能性。

Webview 在多个 VS Code API 中使用:

  • 使用使用创建的 Webview 面板createWebviewPanel。在这种情况下,Webview 面板在 VS Code 中显示为不同的编辑器。这使得它们对于显示自定义 UI 和自定义可视化非常有用。
  • 作为自定义编辑器的视图。自定义编辑器允许扩展提供自定义 UI,用于编辑工作区中的任何文件。自定义编辑器 API 还允许您的扩展挂钩编辑器事件(例如撤消和重做)以及文件事件(例如保存)。
  • 在侧边栏或面板区域中呈现的Webview 视图中。有关更多详细信息,请参阅webview 视图示例扩展

链接

VS Code API 使用

  • window.createWebviewPanel
  • window.registerWebviewPanelSerializer

我应该使用网络视图吗?

Webview 非常令人惊奇,但也应该谨慎使用它们,并且仅在 VS Code 的本机 API 不足时才使用。Webview 占用大量资源,并且在与普通扩展不同的上下文中运行。设计不佳的 WebView 也很容易让人感觉与 VS Code 格格不入。

在使用 webview 之前,请考虑以下事项:

此功能真的需要存在于 VS Code 中吗?作为单独的应用程序或网站会更好吗?

Webview 是实现您的功能的唯一方法吗?您可以改用常规 VS Code API 吗?

您的 webview 会增加足够的用户价值来证明其高资源成本的合理性吗?

请记住:仅仅因为您可以使用网络视图执行某些操作,并不意味着您应该这样做。但是,如果您确信需要使用 webview,那么本文档可以为您提供帮助。让我们开始吧。

Webview API 基础知识

为了解释 webview API,我们将构建一个名为Cat Coding的简单扩展。此扩展将使用 webview 显示一只猫正在编写一些代码的 gif(大概是在 VS Code 中)。当我们使用 API 时,我们将继续向扩展添加功能,包括一个计数器,用于跟踪我们的猫编写了多少行源代码,以及在猫引入错误时通知用户的通知。

这是Cat Codingpackage.json扩展的第一个版本。您可以在此处找到示例应用程序的完整代码。我们扩展的第一个版本提供了一个名为 的命令。当用户调用此命令时,我们将显示一个简单的 Web 视图,其中包含我们的猫。用户将能够从命令面板调用此命令作为Cat 编码:启动新的 cat 编码会话,甚至如果愿意的话,甚至可以为其创建一个键绑定。catCoding.start

js
{
  "name": "cat-coding",
  "description": "Cat Coding",
  "version": "0.0.1",
  "publisher": "bierner",
  "engines": {
    "vscode": "^1.74.0"
  },
  "activationEvents": [],
  "main": "./out/extension.js",
  "contributes": {
    "commands": [
      {
        "command": "catCoding.start",
        "title": "Start new cat coding session",
        "category": "Cat Coding"
      }
    ]
  },
  "scripts": {
    "vscode:prepublish": "tsc -p ./",
    "compile": "tsc -watch -p ./",
    "postinstall": "node ./node_modules/vscode/bin/install"
  },
  "dependencies": {
    "vscode": "*"
  },
  "devDependencies": {
    "@types/node": "^9.4.6",
    "typescript": "^2.8.3"
  }
}

现在让我们执行该catCoding.start命令。在我们的扩展的主文件中,我们注册catCoding.start命令并使用它来显示基本的网络视图:

js
import * as vscode from 'vscode';

export function activate(context: vscode.ExtensionContext) {
  context.subscriptions.push(
    vscode.commands.registerCommand('catCoding.start', () => {
      // Create and show a new webview
      const panel = vscode.window.createWebviewPanel(
        'catCoding', // Identifies the type of the webview. Used internally
        'Cat Coding', // Title of the panel displayed to the user
        vscode.ViewColumn.One, // Editor column to show the new webview panel in.
        {} // Webview options. More on these later.
      );
    })
  );
}

该vscode.window.createWebviewPanel函数在编辑器中创建并显示一个网络视图。catCoding.start如果您尝试在当前状态下运行该命令,您会看到以下内容:

我们的命令打开一个新的 webview 面板,其标题正确,但没有内容!要将我们的猫添加到新面板,我们还需要使用以下方法设置 webview 的 HTML 内容webview.html:

js
import * as vscode from 'vscode';

export function activate(context: vscode.ExtensionContext) {
  context.subscriptions.push(
    vscode.commands.registerCommand('catCoding.start', () => {
      // Create and show panel
      const panel = vscode.window.createWebviewPanel(
        'catCoding',
        'Cat Coding',
        vscode.ViewColumn.One,
        {}
      );

      // And set its HTML content
      panel.webview.html = getWebviewContent();
    })
  );
}

function getWebviewContent() {
  return `<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Cat Coding</title>
</head>
<body>
    <img src="https://media.giphy.com/media/JIX9t2j0ZTN9S/giphy.gif" width="300" />
</body>
</html>`;
}

如果再次运行该命令,现在 Web 视图如下所示:

webview.html应始终是完整的 HTML 文档。HTML 片段或格式错误的 HTML 可能会导致意外行为。

更新网页视图内容

webview.html还可以在创建 webview 后更新其内容。让我们通过引入猫的轮换来使猫编码更加动态:

js
import * as vscode from 'vscode';

const cats = {
  'Coding Cat': 'https://media.giphy.com/media/JIX9t2j0ZTN9S/giphy.gif',
  'Compiling Cat': 'https://media.giphy.com/media/mlvseq9yvZhba/giphy.gif'
};

export function activate(context: vscode.ExtensionContext) {
  context.subscriptions.push(
    vscode.commands.registerCommand('catCoding.start', () => {
      const panel = vscode.window.createWebviewPanel(
        'catCoding',
        'Cat Coding',
        vscode.ViewColumn.One,
        {}
      );

      let iteration = 0;
      const updateWebview = () => {
        const cat = iteration++ % 2 ? 'Compiling Cat' : 'Coding Cat';
        panel.title = cat;
        panel.webview.html = getWebviewContent(cat);
      };

      // Set initial content
      updateWebview();

      // And schedule updates to the content every second
      setInterval(updateWebview, 1000);
    })
  );
}

function getWebviewContent(cat: keyof typeof cats) {
  return `<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Cat Coding</title>
</head>
<body>
    <img src="${cats[cat]}" width="300" />
</body>
</html>`;
}

设置webview.html替换整个 webview 内容,类似于重新加载 iframe。一旦开始在 Web 视图中使用脚本,记住这一点很重要,因为这意味着设置webview.html还会重置脚本的状态。

上面的示例还用于webview.title更改编辑器中显示的文档标题。设置标题不会导致 webview 重新加载。

生命周期

Webview 面板由创建它们的扩展拥有。扩展程序必须保留从 返回的 webview createWebviewPanel。如果您的扩展程序丢失了此引用,则它无法再次重新获得对该 Web 视图的访问权限,即使该 Web 视图将继续显示在 VS Code 中。

与文本编辑器一样,用户也可以随时关闭 Web 视图面板。当用户关闭 webview 面板时,webview 本身就会被销毁。尝试使用已销毁的 webview 会引发异常。这意味着上面使用的示例setInterval实际上有一个重要的错误:如果用户关闭面板,setInterval将继续触发,这将尝试更新panel.webview.html,这当然会抛出异常。猫讨厌例外。让我们解决这个问题!

onDidDispose当 webview 被销毁时会触发该事件。我们可以使用此事件取消进一步更新并清理 webview 的资源:

js
import * as vscode from 'vscode';

const cats = {
  'Coding Cat': 'https://media.giphy.com/media/JIX9t2j0ZTN9S/giphy.gif',
  'Compiling Cat': 'https://media.giphy.com/media/mlvseq9yvZhba/giphy.gif'
};

export function activate(context: vscode.ExtensionContext) {
  context.subscriptions.push(
    vscode.commands.registerCommand('catCoding.start', () => {
      const panel = vscode.window.createWebviewPanel(
        'catCoding',
        'Cat Coding',
        vscode.ViewColumn.One,
        {}
      );

      let iteration = 0;
      const updateWebview = () => {
        const cat = iteration++ % 2 ? 'Compiling Cat' : 'Coding Cat';
        panel.title = cat;
        panel.webview.html = getWebviewContent(cat);
      };

      updateWebview();
      const interval = setInterval(updateWebview, 1000);

      panel.onDidDispose(
        () => {
          // When the panel is closed, cancel any future updates to the webview content
          clearInterval(interval);
        },
        null,
        context.subscriptions
      );
    })
  );
}

扩展程序还可以通过调用 Web 视图以编程方式关闭dispose()它们。例如,如果我们想将猫的工作日限制为五秒:

js
export function activate(context: vscode.ExtensionContext) {
  context.subscriptions.push(
    vscode.commands.registerCommand('catCoding.start', () => {
      const panel = vscode.window.createWebviewPanel(
        'catCoding',
        'Cat Coding',
        vscode.ViewColumn.One,
        {}
      );

      panel.webview.html = getWebviewContent('Coding Cat');

      // After 5sec, programmatically close the webview panel
      const timeout = setTimeout(() => panel.dispose(), 5000);

      panel.onDidDispose(
        () => {
          // Handle user closing panel before the 5sec have passed
          clearTimeout(timeout);
        },
        null,
        context.subscriptions
      );
    })
  );
}

可见性和移动

当 Web 视图面板移至背景选项卡时,它会被隐藏。但它并没有被破坏。webview.html当面板再次进入前台时,VS Code 将自动恢复 webview 的内容:

该.visible属性告诉您 webview 面板当前是否可见。

扩展可以通过调用以编程方式将 webview 面板带到前台reveal()。此方法采用可选的目标视图列来显示面板。Web 视图面板一次只能显示在一个编辑器列中。调用reveal()或拖动 Web 视图面板到新的编辑器列会将 Web 视图移动到该新列中。

让我们更新我们的扩展,以一次只允许一个 webview 存在。如果面板位于后台,则该catCoding.start命令会将其带到前台:

js
export function activate(context: vscode.ExtensionContext) {
  // Track the current panel with a webview
  let currentPanel: vscode.WebviewPanel | undefined = undefined;

  context.subscriptions.push(
    vscode.commands.registerCommand('catCoding.start', () => {
      const columnToShowIn = vscode.window.activeTextEditor
        ? vscode.window.activeTextEditor.viewColumn
        : undefined;

      if (currentPanel) {
        // If we already have a panel, show it in the target column
        currentPanel.reveal(columnToShowIn);
      } else {
        // Otherwise, create a new panel
        currentPanel = vscode.window.createWebviewPanel(
          'catCoding',
          'Cat Coding',
          columnToShowIn || vscode.ViewColumn.One,
          {}
        );
        currentPanel.webview.html = getWebviewContent('Coding Cat');

        // Reset when the current panel is closed
        currentPanel.onDidDispose(
          () => {
            currentPanel = undefined;
          },
          null,
          context.subscriptions
        );
      }
    })
  );
}

这是正在运行的新扩展:

每当 Web 视图的可见性发生变化,或者当 Web 视图移动到新列时,onDidChangeViewState都会触发该事件。我们的扩展可以使用此事件根据 web 视图显示的列来更改猫:

js
const cats = {
  'Coding Cat': 'https://media.giphy.com/media/JIX9t2j0ZTN9S/giphy.gif',
  'Compiling Cat': 'https://media.giphy.com/media/mlvseq9yvZhba/giphy.gif',
  'Testing Cat': 'https://media.giphy.com/media/3oriO0OEd9QIDdllqo/giphy.gif'
};

export function activate(context: vscode.ExtensionContext) {
  context.subscriptions.push(
    vscode.commands.registerCommand('catCoding.start', () => {
      const panel = vscode.window.createWebviewPanel(
        'catCoding',
        'Cat Coding',
        vscode.ViewColumn.One,
        {}
      );
      panel.webview.html = getWebviewContent('Coding Cat');

      // Update contents based on view state changes
      panel.onDidChangeViewState(
        e => {
          const panel = e.webviewPanel;
          switch (panel.viewColumn) {
            case vscode.ViewColumn.One:
              updateWebviewForCat(panel, 'Coding Cat');
              return;

            case vscode.ViewColumn.Two:
              updateWebviewForCat(panel, 'Compiling Cat');
              return;

            case vscode.ViewColumn.Three:
              updateWebviewForCat(panel, 'Testing Cat');
              return;
          }
        },
        null,
        context.subscriptions
      );
    })
  );
}

function updateWebviewForCat(panel: vscode.WebviewPanel, catName: keyof typeof cats) {
  panel.title = catName;
  panel.webview.html = getWebviewContent(catName);
}

检查和调试网络视图

开发人员:切换开发人员工具命令会打开一个开发人员工具窗口,您可以使用该窗口进行调试并检查您的 Web 视图。

请注意,如果您使用的 VS Code 版本早于 1.56,或者如果您尝试调试设置了 的 Webview enableFindWidget,则必须使用Developer: Open Webview Developer Tools命令。此命令为每个 Web 视图打开专用的开发人员工具页面,而不是使用由所有 Web 视图和编辑器本身共享的开发人员工具页面。

在开发人员工具中,您可以使用位于开发人员工具窗口左上角的检查工具开始检查 Web 视图的内容:

您还可以在开发人员工具控制台中查看 Web 视图中的所有错误和日志:

要在 Web 视图上下文中计算表达式,请确保从开发人员工具控制台面板左上角的下拉列表中选择活动框架环境:

活动框架环境是执行 webview 脚本本身的地方。

此外,开发人员:重新加载 Webview命令会重新加载所有活动的 Web 视图。如果您需要重置 Web 视图的状态,或者磁盘上的某些 Web 视图内容已更改并且您希望加载新内容,这会很有帮助。

加载本地内容

Webview 在无法直接访问本地资源的隔离上下文中运行。这样做是出于安全原因。这意味着,为了从您的扩展加载图像、样式表和其他资源,或者从用户当前工作区加载任何内容,您必须使用该函数将Webview.asWebviewUri本地file:URI 转换为 VS Code 可以用来加载的特殊 URI本地资源的子集。

想象一下,我们想要开始将猫 gif 捆绑到我们的扩展中,而不是从 Giphy 中提取它们。为此,我们首先创建磁盘上文件的 URI,然后通过函数传递这些 URI asWebviewUri:

js
import * as vscode from 'vscode';

export function activate(context: vscode.ExtensionContext) {
  context.subscriptions.push(
    vscode.commands.registerCommand('catCoding.start', () => {
      const panel = vscode.window.createWebviewPanel(
        'catCoding',
        'Cat Coding',
        vscode.ViewColumn.One,
        {}
      );

      // Get path to resource on disk
      const onDiskPath = vscode.Uri.joinPath(context.extensionUri, 'media', 'cat.gif');

      // And get the special URI to use with the webview
      const catGifSrc = panel.webview.asWebviewUri(onDiskPath);

      panel.webview.html = getWebviewContent(catGifSrc);
    })
  );
}

function getWebviewContent(catGifSrc: vscode.Uri) {
  return `<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Cat Coding</title>
</head>
<body>
    <img src="${catGifSrc}" width="300" />
</body>
</html>`;
}

本站总访问量次,本站总访客数人次
Released under the MIT License.