Jacky's blog Jacky's blog
首页
  • 编码专题
  • 深入浅出 Vite
  • 深入浅出 babel
  • 快速上手API
  • 深入浅出 react
  • Node

    • code-notebook
  • 状态管理

    • redux
  • 前端工程化

    • Wepack
  • React源码

    • React源码
  • 组件库封装

    • 组件库
  • 开发工具

    • Vscode 插件
  • 项目展示
  • 案例中心 (opens new window)
  • First Project
  • 基础算法题
  • 链表题
  • 动态规划
  • 双指针
  • 递归
  • 数据结构
  • 前端学习计划 (opens new window)
  • 技术随笔
  • 转载文章
  • 包管理工具
  • 前端学习周报
  • VSCode插件
  • Promise 专题
  • 函数技巧
  • React 专题
  • 配置文件

    • TSCONFIG-配置 (opens new window)
    • NGINX-配置 (opens new window)
    • 正则规则查询手册 (opens new window)
    • Lint 配置 (opens new window)
  • 教程

    • GIT-教程
    • NPM SCRIPTS-工作流 (opens new window)
    • DOCKER-教程 (opens new window)
    • LERNA-教程 (opens new window)
    • GIT-常用操作整理 (opens new window)
  • VSCode

    • LAUNCH.JSON (opens new window)
  • 指令

    • NPM 指令 (opens new window)
    • NVM 指令 (opens new window)
    • Nginx 指令 (opens new window)
    • YARN 指令 (opens new window)
    • PNPM 指令 (opens new window)
  • 库

    • FS-EXTRA 库 (opens new window)
    • NODE 库-PATH (opens new window)
  • 永远的神

    • 魔法师卡颂-自顶向下学 React 源码 (opens new window)
    • 全栈潇晨 (opens new window)
    • 博客-程序员山月-Daily (opens new window)
    • 淘系前端:冴羽 (opens new window)
  • 系列文章

    • 《图解HTTP》 (opens new window)
    • 《ES6标准入门》 (opens new window)
    • 《现代JavaScript教程》 (opens new window)
    • 《深入浅出Webpack》 (opens new window)
    • VSCode 插件系列:小茗同学 (opens new window)
    • JEST 教程 (opens new window)
    • 前端精读周刊:各种精读系列 (opens new window)
    • 一文吃透系列 (opens new window)
    • 图解 REACT 原理 (opens new window)
  • 实用网站

    • MDN (opens new window)
    • CAN I USE (opens new window)
    • TYPESCRIPT-ESLint-RULES (opens new window)
    • ESLint-RULES (opens new window)
    • FRONT-END TREND (opens new window)
    • NPM TREND (opens new window)
    • 在线分析 Node 依赖 (opens new window)
    • FIND NPM (opens new window)
    • CODE PEN (opens new window)
    • 印记中文 (opens new window)
    • TOOL.LU (opens new window)
    • 阮一峰-网道 (opens new window)
    • DIGITAL OCEAN (opens new window)
    • DEVDOCS.IO (opens new window)
    • JOI (opens new window)
  • 算法

    • 小浩算法 (opens new window)
    • LABULADONG 的算法小抄 (opens new window)
    • 力扣 SOLUTION (opens new window)
    • HACKER RANK (opens new window)
    • 代码随想录 (opens new window)
  • 博客系列

    • 美团大佬 (opens new window)
    • 蜡笔小伟 (opens new window)
    • 优秀博客1 (opens new window)
    • 优秀博客2-umi (opens new window)
    • 优质博客 (opens new window)
  • CSS

    • CSS-EASING 库 (opens new window)
    • ROUGH.JS (opens new window)
    • CSS 网站收集
    • UNOCSS (opens new window)
  • 前端

    • PROMISE (opens new window)
    • UNDERSCORE.JS (opens new window)
    • study with BGM (opens new window)
    • nginx【B站视频】 (opens new window)
    • 机器学习
    • Js基础
  • 掘金已购课程

    • 前端自动化测试精讲 (opens new window)
    • 深入浅出 Vite (opens new window)
    • 现代 Web 布局 (opens new window)
    • 前端算法与数据结构 (opens new window)
    • 基于 Vite 的 SSG 框架开发实战 (opens new window)
    • SSR 实战:官网开发指南 (opens new window)
    • WebGL 入门与实践 (opens new window)
    • 玩转 CSS 的艺术之美 (opens new window)
    • 前端调试通关秘籍 (opens new window)
    • React 进阶实践指南 (opens new window)
    • TypeScript 全面进阶指南 (opens new window)
    • 前端缓存技术与方案解析 (opens new window)
    • npm scripts 前端工作流 (opens new window)
    • Webpack5 核心原理与应用实践 (opens new window)
  • 购物车

    • 张鑫旭-技术写作指南 (opens new window)
    • 深入剖析 Node.js 底层原理 (opens new window)
    • 前端开发者的现代 C++ 课 (opens new window)
    • 从前端到全栈 (opens new window)
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Jacky Wang

行到水穷处,坐看云起时
首页
  • 编码专题
  • 深入浅出 Vite
  • 深入浅出 babel
  • 快速上手API
  • 深入浅出 react
  • Node

    • code-notebook
  • 状态管理

    • redux
  • 前端工程化

    • Wepack
  • React源码

    • React源码
  • 组件库封装

    • 组件库
  • 开发工具

    • Vscode 插件
  • 项目展示
  • 案例中心 (opens new window)
  • First Project
  • 基础算法题
  • 链表题
  • 动态规划
  • 双指针
  • 递归
  • 数据结构
  • 前端学习计划 (opens new window)
  • 技术随笔
  • 转载文章
  • 包管理工具
  • 前端学习周报
  • VSCode插件
  • Promise 专题
  • 函数技巧
  • React 专题
  • 配置文件

    • TSCONFIG-配置 (opens new window)
    • NGINX-配置 (opens new window)
    • 正则规则查询手册 (opens new window)
    • Lint 配置 (opens new window)
  • 教程

    • GIT-教程
    • NPM SCRIPTS-工作流 (opens new window)
    • DOCKER-教程 (opens new window)
    • LERNA-教程 (opens new window)
    • GIT-常用操作整理 (opens new window)
  • VSCode

    • LAUNCH.JSON (opens new window)
  • 指令

    • NPM 指令 (opens new window)
    • NVM 指令 (opens new window)
    • Nginx 指令 (opens new window)
    • YARN 指令 (opens new window)
    • PNPM 指令 (opens new window)
  • 库

    • FS-EXTRA 库 (opens new window)
    • NODE 库-PATH (opens new window)
  • 永远的神

    • 魔法师卡颂-自顶向下学 React 源码 (opens new window)
    • 全栈潇晨 (opens new window)
    • 博客-程序员山月-Daily (opens new window)
    • 淘系前端:冴羽 (opens new window)
  • 系列文章

    • 《图解HTTP》 (opens new window)
    • 《ES6标准入门》 (opens new window)
    • 《现代JavaScript教程》 (opens new window)
    • 《深入浅出Webpack》 (opens new window)
    • VSCode 插件系列:小茗同学 (opens new window)
    • JEST 教程 (opens new window)
    • 前端精读周刊:各种精读系列 (opens new window)
    • 一文吃透系列 (opens new window)
    • 图解 REACT 原理 (opens new window)
  • 实用网站

    • MDN (opens new window)
    • CAN I USE (opens new window)
    • TYPESCRIPT-ESLint-RULES (opens new window)
    • ESLint-RULES (opens new window)
    • FRONT-END TREND (opens new window)
    • NPM TREND (opens new window)
    • 在线分析 Node 依赖 (opens new window)
    • FIND NPM (opens new window)
    • CODE PEN (opens new window)
    • 印记中文 (opens new window)
    • TOOL.LU (opens new window)
    • 阮一峰-网道 (opens new window)
    • DIGITAL OCEAN (opens new window)
    • DEVDOCS.IO (opens new window)
    • JOI (opens new window)
  • 算法

    • 小浩算法 (opens new window)
    • LABULADONG 的算法小抄 (opens new window)
    • 力扣 SOLUTION (opens new window)
    • HACKER RANK (opens new window)
    • 代码随想录 (opens new window)
  • 博客系列

    • 美团大佬 (opens new window)
    • 蜡笔小伟 (opens new window)
    • 优秀博客1 (opens new window)
    • 优秀博客2-umi (opens new window)
    • 优质博客 (opens new window)
  • CSS

    • CSS-EASING 库 (opens new window)
    • ROUGH.JS (opens new window)
    • CSS 网站收集
    • UNOCSS (opens new window)
  • 前端

    • PROMISE (opens new window)
    • UNDERSCORE.JS (opens new window)
    • study with BGM (opens new window)
    • nginx【B站视频】 (opens new window)
    • 机器学习
    • Js基础
  • 掘金已购课程

    • 前端自动化测试精讲 (opens new window)
    • 深入浅出 Vite (opens new window)
    • 现代 Web 布局 (opens new window)
    • 前端算法与数据结构 (opens new window)
    • 基于 Vite 的 SSG 框架开发实战 (opens new window)
    • SSR 实战:官网开发指南 (opens new window)
    • WebGL 入门与实践 (opens new window)
    • 玩转 CSS 的艺术之美 (opens new window)
    • 前端调试通关秘籍 (opens new window)
    • React 进阶实践指南 (opens new window)
    • TypeScript 全面进阶指南 (opens new window)
    • 前端缓存技术与方案解析 (opens new window)
    • npm scripts 前端工作流 (opens new window)
    • Webpack5 核心原理与应用实践 (opens new window)
  • 购物车

    • 张鑫旭-技术写作指南 (opens new window)
    • 深入剖析 Node.js 底层原理 (opens new window)
    • 前端开发者的现代 C++ 课 (opens new window)
    • 从前端到全栈 (opens new window)
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Node

  • VSCode插件

    • 如何打包 VSCode IDE(Mac)
    • 如何查看 VSCode 官网
    • VSCode目录结构分析
    • VSCode插件-yo脚手架创建
    • VSCode插件-debug
    • VSCode插件-TreeView
    • VSCode插件-WebView
      • 0.前言
      • 1.实战技巧
        • 1.1. 如何创建一个WebView,并使其支持 js 功能
        • 1.2.更新页面内容
        • 1.3.面板生命周期:销毁|隐藏
        • 1.4.本地资源的加载
        • 1.5.Extension 与 WebView 的通信
        • 1.6.状态持久化(Persistence)
      • 2.参考资料
    • VSCode插件-i18n
    • VSCode插件-Color Theme
    • VSCode插件-Node进程交互
    • VSCode 插件-Edge_devtools 源码
    • VSCode插件-使用 gulp 打包插件
    • VSCode-Tasks模式
    • VSCode插件-存储方式
    • VSCode插件-开发 code-snippets 插件
  • Webpack

  • Redux

  • React源码

  • 组件库

  • React高阶系列

  • UMI插件

  • 前端工程化

  • 单元测试vitest

  • 重点技术
  • VSCode插件
wangjiasheng
2022-03-07
目录

VSCode插件-WebView

# 0.前言

本篇博客记录的是 VSCode 插件的 WebView 模块。

记录 webview 常用操作及解决方案如下:

  1. webview 的基础创建,以及常见 Opition 说明。
  2. 更新页面内容。
  3. 通过生命周期函数可以实现对webview页面事件的捕获,如:colorTheme、销毁事件 等。
  4. 本地资源的加载方案*
  5. Extension与 WebView 的通信*
  6. 当 VSCode 被异常关闭后,重新打开后如何维持原有的 webview 状态。

# 1.实战技巧

# 1.1. 如何创建一个WebView,并使其支持 js 功能

// Create and show a new webview
const panel = vscode.window.createWebviewPanel(
  'catCoding', // 标识 webview 的type (在内部使用)
  'Cat Coding', // Tabs 的 Title
  vscode.ViewColumn.One, // 指定展示的区域(如,第1个Col)
  { enableScripts: true } // Webview 的 options
);
1
2
3
4
5
6
7

注意,此时这一步只是创建了面板,需要指定html显示页面,在这里使用字符串的形式导入文件(因为在VSCode插件中引入外部文件会受到安全限制的影响,后面会单独讲解)。

// 将 html 挂载到 panel 面板
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>`;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

html 默认无法执行 js 代码,需要在 createWebviewPanel 打开enableScripts 属性。

# 1.2.更新页面内容

非常简单,对panel.webview.html 重新赋值 html 字符串。

这里产生一个疑问,这里存在性能问题吧,VScode给的例子是一个不断变化的猫的图片,每次变化都需要重新生成这段代码,而且也没有 diff 操作。

const updateWebview = () => {
  panel.title = cat;
  panel.webview.html = getWebviewContent(cat);
};
1
2
3
4

# 1.3.面板生命周期:销毁|隐藏

使用 createWebviewPanel 创建 panel 后,我们可以捕获该 panel 状态:

  1. 销毁(dispose):可以通过 panel.onDidpose 生命周期捕获。

  2. 隐藏(Visibility)和移动(Moving):都可以通过onDidChangeViewState 生命周期捕获。

    • 对于隐藏状态,可以查看 panel.visiblity
    • 对于隐藏状态的webveiw ,可以调用 panel.reveal() 重新唤醒
    • webview 是否被销毁过,可以查看 panel._store._isDisposed。

    更多的属性查看,请通过断点Debug调试。

使用 onDidDispose 实现 WebView的 重新激活

let currentPanel = vscode.WebviewPanel | undefined // 初始化

const columnToShowIn = vscode.window.activeTextEditor
? vscode.window.activeTextEditor.viewColumn
: undefined;

if (currentPanel) {
  // 如果已存在变量,则重新激活
  currentPanel.reveal()
  // currentPanel.reveal(columnToShowIn); // reveal 还行可以指定激活的区域
} else {
  // create a new panel
  currentPanel = vscode.window.createWebviewPanel(
    'webviewID', // 标识 webview 的 ID, 如重启VSCOde后维护窗体时需要此字段。
    'Cat Coding', // webview 的 tab title
    columnToShowIn,
    {
      enableScripts: true,
      retainContextWhenHidden: true
    }
  );
  // 渲染 猫 的页面
  currentPanel.webview.html = getWebviewContent('Coding Cat');

  // 关闭 webView 的时候清空实例对象。
  currentPanel.onDidDispose(
    () => {
      currentPanel = undefined;
    },
    null,
    context.subscriptions
  );
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

使用onDidChangeViewState 更新 webview

panel.onDidChangeViewState(
  e => {
    // 通过 e.webviewPanel 
    const panel = e.webviewPanel;
    .....
  },
  null,
  context.subscriptions
);
1
2
3
4
5
6
7
8
9

结合switch case语法

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
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 1.4.本地资源的加载

VScode 插件不允许直接加载本地资源,意味着 panel.webview.html 内部所有的外部资源都需要重新处理。

处理步骤如下:

  1. 拼接绝对路径地址

    import * as path from "vscode"
    path.join(context.extensionPath, 'media', 'cat.gif')
    // 推荐使用 path.resolve()
    
    1
    2
    3
  2. 添加 file:// 文件协议头

    // DiskPath 磁盘路径
    const onDiskPath = vscode.Uri.file(
      path.join(context.extensionPath, 'media', 'cat.gif')
    );
    
    1
    2
    3
    4
  3. 转化为VScode 的协议头:vscode-resource:/

    const catGifSrc = panel.webview.asWebviewUri(onDiskPath);
    // vscode-resource:/Users/toonces/projects/vscode-cat-coding/media/cat.gif
    
    1
    2
  4. 最后一步,将生成的新协议的地址插入到 html 中,重构 html 页面。

    <img src="${catGifSrc}" width="300" />
    
    1

控制资源的访问权限

上面的例子中,是将位于静态文件夹下(./media/cat.svg)猫的图片插入到 html 中,我们还可以限制资源访问的文件夹,如只允许读取 media 中的图片资源。

const panel = vscode.window.createWebviewPanel(
  'catCoding',
  'Cat Coding',
  vscode.ViewColumn.One,
  {
    // 只允许读取 `media` 中的图片资源。
    localResourceRoots: [vscode.Uri.file(path.join(context.extensionPath, 'media'))]
  }
);
1
2
3
4
5
6
7
8
9

如果不允许读取所有本地资源,可以直接将 localResourceRoots 设置为[]

# 1.5.Extension 与 WebView 的通信

情况1:Extensions →\rightarrow→ WebView

Extensions往WebView传递信息可以通过:webview.postMessage({...}),如传递command

// 注册一个新的 command
context.subscriptions.push(
  vscode.commands.registerCommand('catCoding.doRefactor', () => {
    if (!currentPanel) {
      return;
    }
    // 可以传递所以可以被`JSON`序列化的内容
    currentPanel.webview.postMessage({ command: 'refactor' });
})
1
2
3
4
5
6
7
8
9

WebView接受信息,即,让HTML 接受这个 Message

方式:全局监听 message 事件。

<script>
  // 在 HTML 内部的 sript 监听 `message` 事件
  window.addEventListener('message', event => {
    const message = event.data; // event.data 是一个 JSON 对象
    switch (message.command) {
      case 'refactor':
        ..... 
        break;
    }
  });
</script>
1
2
3
4
5
6
7
8
9
10
11

注:为了让 HTML支持JS代码,在创建webview时,设置{enableSripte: true}

情况2:WebView →\rightarrow→ Extensions

在 HTML 的 <script> 有一个内置的函数acquireVSCodeApi,可以得到vscode对象,通过调用 vscode.postMessage 函数向Extensions传递。

<script>
  (function() {
  const vscode = acquireVsCodeApi();
    if (Math.random() < 0.001 * count) {
      vscode.postMessage({
        command: 'alert',
        text: '🐛  on line ' + count
      })
    }
  }, 100);
}())
</script>
1
2
3
4
5
6
7
8
9
10
11
12

Extensions 通过onDidReceiveMessage 钩子函数获取信息:

// Extensions 接受 Message
panel.webview.onDidReceiveMessage(
  message => {
    switch (message.command) {
      case 'alert':
        vscode.window.showErrorMessage(message.text);
        return;
    }
  },
  undefined,
  context.subscriptions
);
1
2
3
4
5
6
7
8
9
10
11
12

总结:

  1. Extensions 发:panel.webview.postMessage()

  2. Extensions收:panel.webview.onDidReceiveMessage()

  3. Webview 发:vscode.postMessage() (vscode 通过内置的 acquireVsCodeApi 获取)

  4. Webview 收: windows.addEventListener("message",event=>{})

# 1.6.状态持久化(Persistence)

首先明确有哪儿些状态需要被缓存:

  1. webview 窗体本身:easy,只需要记忆panel对象即可。

    注:该操作需要维持一个全局的panel变量用于保存窗体对象,如果丢失了窗体对象,就无法重新获取。

  2. webview 窗体被隐藏(panel.visible)或者被销毁(panel.dispose)时,webview 嵌入的html内部所有的状态会被丢失。

  3. 整个VSCode软件退出,重启后,需要重新显示webview内容。

# 方案1:维护窗体本身

维护一个全局变量

let panel = undefined // 初始化

function activate(context) {
  	if (panel) {
      // 如果存在 panel 变量,重新激活窗体
      panel.reveal();
    }else{
      // panel undefined时,创建一个新的窗体
      panel = vscode.window.createWebviewPanel(
      "webview_type",
      "Tabs Title",
       1, // 显示的 Col
       { enableSript: true} // 各种 Options
      );
    }
   // 关闭时,销毁 panel 变量
   panel.onDidDispose(()=>{ panel = undefined},null,context.subscriptions)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 方案2:窗口隐藏时,仍维持状态

这简单的方案,在创建 webview 时设置options

panel = vscode.window.createWebviewPanel(
  "webview_type",
  "Tabs Title",
   1, 
  { 
    enableSript: true,
  	retainContextWhenHidden: true 
  } 
);
1
2
3
4
5
6
7
8
9

设置 retainContextWhenHidden 后就无法再与 hidden窗体进行通信。

# 方案3:vscode重启后,维护窗体。

在创建 webview 时,一直不理解第一个string 标识,原文中称该变量为webview type,用于内部对窗体的标识(Used internally),具体实践步骤如下:

  1. 创建新webview时,指定webview type

    const panel = vscode.window.createWebviewPanel("webview_id","Tabs Title",1,{})
    
    1
  2. 指定 vscode 重启后,触发的窗体

    vscode.window.registerWebviewPanelSerializer(
      'webview_type',
      new RetainContextSerializer()
    );
    
    1
    2
    3
    4

    当VSCode 重启后,将会调用由 RetainContextSerializer 构造的窗体。

  3. 上述构造函数中的内容,

    重启后需要显示的页面:showCatAndCount()

    通过 webviewPanel 可以拿到 restore 前的面板对象,重新赋值上页面内容即可。

    class RetainContextSerializer {
    	async deserializeWebviewPanel(webviewPanel, state) {
    		webviewPanel.webview.html = showCatAndCount();
    	}
    }
    
    1
    2
    3
    4
    5
  4. 最后需要在package.json中设置

    "activationEvents": [
        ...,
        "onWebviewPanel:catCoding"
    ]
    
    1
    2
    3
    4

# 2.参考资料

  1. VSCode官方插件教程-Webview (opens new window)
编辑 (opens new window)
#VSCode插件
上次更新: 2023/06/17, 10:06:00
VSCode插件-TreeView
VSCode插件-i18n

← VSCode插件-TreeView VSCode插件-i18n→

最近更新
01
如何理解浏览器的 user agent 用户代理的含义?
11-05
02
浏览器事件循环机制
10-31
03
浏览器页面渲染机制【2023】
10-15
更多文章>
Theme by Vdoing | Copyright © 2020-2023
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式