vscode webview插件开发指南(一)

256 阅读4分钟

背景

近期团队开始着手对代码质量进行治理,而在治理中最先开始的就代码规约的治理,对于所使用的规约包中的强制规约进行治理。由于目前内部规约扫描的脚手架扫描后生成的规约报告只有json格式,并且该json极其庞大,这导致在治理的时候智能通过页面的搜索功能去搜索强制性的规约再逐一修复。经常出现搜索变化重新搜索后忘记自己上回改动的问题处于第几个搜索结果。于是在经过比对各种方案后,题主选择了利用vscode webview的方案来可视化展示规约扫描结果的方案。以下是在开发过程中遇到的一些问题,再次记录并分享。

写在最前

本篇文章只是一个开发一个插件最基本的工程初始化以及对工程的改造,使得我们可以像正常使用react一样去开发一个vscode webview,在实际开发中我们会有更多的需求,比如说页面通信,路由配置等。

1. 所需工具

2. 工程初始化

  1. 安装 generator-codevsce 脚手架
npm i generator-code vsce -g
  1. 初始化工程并根据需要选择配置
yo code

以下是题主本次开发所选的配置 image.png

  1. 工程结构及部分关键文件解释
.vscode
    - extensions.json
    - launch.json
    - settings.json
    - tasks.json
src
    - test
    - extension.ts
.vscodeignore
package.json
tsconfig.json
webpack.config.js
  • src/test: 插件单测文件
  • src/extension.ts: 插件的入口文件,在该文件中对外暴露插件激活与失效时的方法
  • .vscodeignore文件: 在后期发布的时候该文件中包含的文件不会被上传至插件市场

3. 工程改造,支持react开发

由于目前大多数的开发都是基于第三方的框架来进行页面的开发,在项目初始化的时候官方并未提供使用第三方框架开发webview的模版工程,接下来我们对官方的工程进行改造,让我们的工程支持使用react框架来开发。为此我们需要新增一些依赖。

npm install react react-dom react-router-dom -S
npm install  @types/react @types/react-dom @types/react @types/react-router-dom webpack webpack-cli ts-loader css-loader style-loader npm-run-all -D

首先我们需要在根目录创建app文件夹,该文件夹用来存放我们的webview页面开发相关的内容,并给该文件夹单独配置一份tsconfig.json

  • app/tsconfig.json
{
  "compilerOptions": {
    "module": "ESNext",
    "allowSyntheticDefaultImports": true,
    "moduleResolution": "node",
    "target": "ES5",
    "jsx": "react",
    "sourceMap": true,
    "experimentalDecorators": true,
    "lib": ["dom", "ES2015"],
    "strict": true
  }
}
  • app/index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import Home from "./pages/Home";

ReactDOM.render(<Home />, document.getElementById('root'));
  • app/pages/Home/index.tsx
import React, { useEffect, useState } from 'react';

const Home = () => {
    return <div>this is home page</div>
}

export default Home;

  • webpack.config.js: 负责app文件的编译
const path = require('path');

module.exports = {
  entry: path.join(__dirname, 'app', 'index.tsx'),
  resolve: {
    extensions: ['.ts', '.tsx', '.js', '.jsx', '.css'],
  },
  devtool: 'inline-source-map',
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: 'ts-loader',
        exclude: '/node_modules/',
      },
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader'],
      },
    ],
  },
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist', 'app'),
  },
};

  • tsconfig.json 这样我们的ts就不会编译app文件夹,而把app文件夹的构建交给了webpack
{
    "compilerOptions": {
        "module": "commonjs",
        "target": "es6",
        "outDir": "dist",
        "lib": [
                "es6"
        ],
        "sourceMap": true,
        "rootDir": ".",
        "strict": true   
    },
    "exclude": [
        "node_modules",
        "app"
    ]
}
"scripts": {
    "vscode:prepublish": "npm run compile",
    "compile": "npm-run-all compile:*",
    "compile:extension": "tsc -p ./",
    "compile:view": "webpack --mode development",
    "watch": "npm-run-all -p watch:*",
    "watch:extension": "tsc -watch -p ./",
    "watch:view": "webpack --watch --mode development",
    "pretest": "npm run compile && npm run lint",
    "lint": "eslint src --ext ts",
    "test": "node ./dist/test/runTest.js"
  },

4. 创建webview

4.1 命令注册

通过以下的配置我们就完成了demo.show命令的注册,安装插件后即可通过demo.show命令来唤起我们的webview页面。

// package.json
  "activationEvents": [
    "onCommand: demo.show"
  ],
  "contributes": {
    "commands": [
      {
        "command": "demo.show",
        "title": "show demo webview"
      }
    ]
  },
  
// src/extension.ts

export function activate(context: vscode.ExtensionContext) {
  context.subscriptions.push(
    vscode.commands.registerCommand('demo.show', () => {
      ViewLoader.showWebview(context);
    })
  );
}

4.2 ViewLoader实现

import * as vscode from 'vscode';
import * as path from 'path';

export default class ViewLoader {
  public static currentPanel?: vscode.WebviewPanel;
  private panel: vscode.WebviewPanel;
  private context: vscode.ExtensionContext;
  private disposables: vscode.Disposable[];

  private viewType =  'lint-bp';
  private title = '代码规约分析';

  constructor(context: vscode.ExtensionContext) {
    this.context = context;
    this.disposables = [];

    this.panel = vscode.window.createWebviewPanel(this.viewType, this.title, vscode.ViewColumn.One, {
      enableScripts: true,
      retainContextWhenHidden: true,
      localResourceRoots: [
        vscode.Uri.file(path.join(this.context.extensionPath, 'dist', 'app')),
      ],
    });

    this.renderWebview();

    this.panel.onDidDispose(
      () => {
        this.dispose();
      },
      null,
      this.disposables
    );
  }

  private renderWebview() {
    const html = this.render();
    this.panel.webview.html = html;
  }

  static showWebview(context: vscode.ExtensionContext) {
    const cls = this;
    const column = vscode.window.activeTextEditor
      ? vscode.window.activeTextEditor.viewColumn
      : undefined;
    if (cls.currentPanel) {
      cls.currentPanel.reveal(column);
    } else {
      cls.currentPanel = new cls(context).panel;
    }
  }

  public dispose() {
    ViewLoader.currentPanel = undefined;

    // Clean up our resources
    this.panel.dispose();

    while (this.disposables.length) {
      const x = this.disposables.pop();
      if (x) {
        x.dispose();
      }
    }
  }

  private render() {
    const bundleScriptPath = this.panel.webview.asWebviewUri(
      vscode.Uri.file(path.join(this.context.extensionPath, 'dist', 'app', 'bundle.js'))
    );
    return `
      <!DOCTYPE html>
        <html lang="en">
        <head>
          <meta charset="UTF-8">
          <meta name="viewport" content="width=device-width, initial-scale=1.0">
          <title>Lint Bp</title>
        </head>
    
        <body>
          <div id="root"></div>
          <script src="${bundleScriptPath}"></script>
        </body>
      </html>
    `;
  }
}

最后

通过本文大家将了解如何在vscode插件项目中使用我们熟悉的第三方框架进行webview的开发,这只是开发中最基础的一步,后续题主将会更新更多关于vscode插件开发webview通信以及后续发布相关的文章,本文有错误的地方欢迎指正,如果有更多vscode插件开发相关的问题也欢迎留言评论或私信探讨

参考

code.visualstudio.com/api/extensi… code.visualstudio.com/api/extensi…