随笔

VS Code Extension Of Webview

Webview 的功能想必不用过多赘述,就像微信小程序一样借助于 VS Code 平台承载页面,一般用来做可视化,或者比较复杂的配置页面,当然你非要把自己的工具站塞到这里也行,但就和小程序嵌 H5 一样,何必多此一举呢?

配置启动信息

当编辑的文件为指定语言时,将 Start a visual preview 注册到命令菜单和右键菜单中,选择该菜单时,会广播 preview.start 事件

"commands": [
    {
        "command": "preview.start",
        "title": "Start a visual preview"
    }
],
"menus": {
  "commandPalette": [
    {
      "command": "preview.start",
      "when": "editorLangId == language id"
    }
  ],
    "editor/context": [
      {
        "command": "preview.start",
        "when": "editorLangId == antlr"
      }
    ]
}

注册命令监听

context.subscriptions.push(
    vscode.commands.registerCommand("preview.start", () => {
        // ......
    })
)

创建 Webview

vscode.ViewColumn.Two 代表的是分屏创建,如果想在新页面打开,使用 vscode.ViewColumn.One 即可

const panel = vscode.window.createWebviewPanel(
    "preview",
    previewConfig.title,
    vscode.ViewColumn.Two,
    { enableScripts: true }
);

如果需要启动时获取当前正在编辑的文本,可以通过访问 vscode.window.activeTextEditor?.document.getText() 获取,后续更新可以通过诸如 vscode.workspace.onDidSaveTextDocument 保存后或者变更后发送数据到页面

加载 HTML

let html = readFileSync(
    vscode.Uri.file(
      path.join(context.extensionUri.path, "templates/preview.html")
    ).with({ scheme: "vscode-resource" }).fsPath,
    "utf-8"
);

加载外部资源

const getResourceUri = (name: string) => {
  return panel?.webview.asWebviewUri(
    vscode.Uri.joinPath(context.extensionUri, 'templates/media', name)
  )
}

const getNonce() {
  let text = "";
  const possible =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
  for (let i = 0; i < 32; i++) {
    text += possible.charAt(Math.floor(Math.random() * possible.length));
  }
  return text;
}

const nonce = getNonce();
const scripts = ['xxx.js', 'xxxx.js', 'main.js'].map(name => `
        <script nonce="${nonce}" src="${getResourceUri(name)}"></script>`).join('');
const styles = `
    <link href="${getResourceUri("main.css")}" rel="stylesheet">
  `;

html = html
  .replace("'${initData}'", JSON.stringify(data))
  .replace("${scripts}", scripts)
  .replace("${cspSource}", panel.webview.cspSource)
  .replace(/(\$\{nonce\})/g, nonce)
  .replace("${styles}", styles);

panel.webview.html = html;

其中的 nonce 和 cspSource 是因为限制的安全等级比较高,所以必须要配置相应的 CSP 规则才能使用。

csp error.png

例如上面的报错截图,就必须要配置 connect-src 才可以访问

通信

通过 postMessage 进行双向通信

panel.webview.onDidReceiveMessage((e) => {

})

panel.webview.postMessage({
  type: 'update:data',
  data: {}
})

window.onmessage = (e) => {
    const { type, data } = e.data;
}

使用

打开目标文件,唤出命令面板,输入 > 出现配置的命令,选择注册的菜单,或者编辑器中按右键也会出现改菜单。

Screenshot 2023-08-23 at 17.39.35.png

Screenshot 2023-08-25 at 10.39.33.png

按下菜单后,自动在编辑器右侧打开 Webview

Screenshot 2023-08-23 at 17.43.10.png

本文链接:https://note.lilonghe.net//post/vscode-extension-of-webview.html

-- EOF --