如何使用 React、TypeScript、Tailwind 创建 VS Code 扩展

简介

本文专门针对那些喜欢名为VS Code的代码编辑器的人,该编辑器一直被低估且未被充分使用。

代码编辑器内部有一个扩展部分,可以在编写代码片段(自动完成)、调试主题等更有用的扩展时提供额外的帮助。

例如,Postman VS Code 扩展使您能够直接从 Visual Studio Code 在 Postman 中开发和测试 API。

图片[1]-如何使用 React、TypeScript、Tailwind 创建 VS Code 扩展-哈德森博客

在上面的扩展中,显示的 UI 位于 VS Code 内部,这得益于 Postman 扩展的帮助。因为它似乎也是代码编辑器不可或缺的一部分,但可以肯定的是,这是仅使用htmlcssjavascript制作的,任何人都可以轻松制作。

为了确保您只需按 ,Ctrl + Shift + P然后选择Developer: Toggle Developer Tools显示 VS Code 中的开发工具。

图片[2]-如何使用 React、TypeScript、Tailwind 创建 VS Code 扩展-哈德森博客

正如上面的 gif 中提到的,人们可以像检查任何网页一样轻松地检查任何扩展。现在到了重要的部分,如何进行这样的扩展。

让我们从它开始......


了解扩展创建

为了创建扩展,VS Code 使用名为YeomanCode-Generator 的cli 工具。

在开始之前,您需要安装 npm 并了解 Node 的基本知识。

全局安装包

npm install --global yo generator-code

启动扩展安装

yo code

之后,您将看到这个 Yeomen 扩展菜单


     _-----_     ╭──────────────────────────╮
    |       |    │   Welcome to the Visual  │
    |--(o)--|    │   Studio Code Extension  │
   `---------´   │        generator!        │
    ( _´U`_ )    ╰──────────────────────────╯
    /___A___\   /
     |  ~  |     
   __'.___.'__   
 ´   `  |° ´ Y ` 

? What type of extension do you want to create? (Use arrow keys)
> New Extension (TypeScript) 
  New Extension (JavaScript)
  New Color Theme
  New Language Support
  New Code Snippets
  New Keymap
  New Extension Pack
  New Language Pack (Localization)
  New Web Extension (TypeScript)
  New Notebook Renderer (TypeScript)

选择不同类型的扩展,但在我们的例子中,我们将使用某些选项

? What type of extension do you want to create? New Extension (TypeScript)
? Whats the name of your extension? HelloWorld
? Whats the identifier of your extension? helloworld
? Whats the description of your extension? LEAVE BLANK
? Initialize a git repository? Yes
? Bundle the source code with webpack? No
? Which package manager to use? npm

? Do you want to open the new folder with Visual Studio Code? Open with `code`

在编辑器中,打开src/extension.ts并按F5。这将在新的扩展开发主机窗口中编译并运行扩展。

从新窗口中的Hello World命令面板运行命令Ctrl + Shift + P

输出将产生 VS Code 信息消息(默认):Hello World

你可以通过改变里面的东西来玩src/extension.ts

// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
import * as vscode from 'vscode';

// This method is called when your extension is activated
// Your extension is activated the very first time the command is executed
export function activate(context: vscode.ExtensionContext) {

    // Use the console to output diagnostic information (console.log) and errors (console.error)
    // This line of code will only be executed once when your extension is activated
    console.log('Congratulations, your extension "helloworld" is now active!');

    // The command has been defined in the package.json file
    // Now provide the implementation of the command with registerCommand
    // The commandId parameter must match the command field in package.json
    let disposable = vscode.commands.registerCommand('helloworld.helloWorld', () => {
        // The code you place here will be executed every time your command is executed
        // Display a message box to the user
        vscode.window.showInformationMessage('Hello World from Helloworld!');
    });

    context.subscriptions.push(disposable);
}

// This method is called when your extension is deactivated
export function deactivate() {}

该文件是您的扩展的基础。它包含 2 个重要的方法:activatedeactivate。导入的vscode.command模块注册命令和上下文参数共同订阅它们。

package.json

{
...,
  "activationEvents": [],
  "contributes": {
    "commands": [
      {
        "command": "helloworld.helloWorld",
        "title": "Hello World"
      }
    ]
  },
}

Contributes.command:帮助在命令面板中显示带有标题的命令,还包含视图等。

激活事件:在特定条件下自动触发已注册的命令

为了更清楚地了解,您可以访问您的第一个 VS Code 扩展

但是,本文不仅仅涉及任何正常的扩展。对于 UI 部分,我们将通过 VS Code使用WebView 。


用于 UI 的 Webview API

Webview 允许扩展在 Visual Studio Code 中创建完全可自定义的视图。要快速实施,请按照以下步骤操作:使用以下代码
覆盖文件:src/extension.ts

import * as vscode from 'vscode';

export function activate(context: vscode.ExtensionContext) {

    console.log('Congratulations, your extension "Webview" is up and running now');

    let webview = vscode.commands.registerCommand('helloworld.webview', () => {

        let panel = vscode.window.createWebviewPanel("webview", "Web View", {
            viewColumn: vscode.ViewColumn.One,
        })

        // will set the html here
                 panel.webview.html = `<h1>This is Heading 1</h1>
    <h2>This is Heading 2</h2>
    <h3>This is Heading 3</h3>
    <h4>This is Heading 4</h4>
    <h5>This is Heading 5</h5>`

    });

    });

    context.subscriptions.push(webview);
}

export function deactivate() { }

需要注意的要点:

  1. 为了借助命令创建 Webview 面板helloworld.webview,我们需要使用它的回调来注册它。panel用于将 html 设置到 webview 中。
  2. 在回调中,vscode.window.createWebviewPanel调用方法,该方法需要 4 个参数,,viewTypetitlewebview 面板的标题),showOption(哪个列和焦点),options(重要的是,因为它处理安全性,如脚本编写和面板资源的可访问性)并且它回报vscode.WebviewPanel
  3. 重要步骤:如果您注册了新命令,请始终记住将其添加到package.jsoncontributescommands
{
...,
"contributes": {
    "commands": [
      {
        "command": "helloworld.webview",
        "title": "Web View"
      }
    ]
  },
}

然后,使用 运行扩展F5,最终会打开另一个 vscode 窗口Extension Development。然后按,在命令面板中Ctrl + Shift + P输入 on (或您在 package.json 中提到的命令标题)。Web View

出现的窗口必须如下所示

图片[3]-如何使用 React、TypeScript、Tailwind 创建 VS Code 扩展-哈德森博客

设计用户界面

样式与通常使用 css 的网页相同,但有一个问题,如果您在外部编写 css,则需要首先允许该资源。

默认情况下,扩展位置和工作区中的所有本地资源都是可访问的。createWebviewPanel要控制它们,只需通过提供第四个参数来更改代码即可localResourceRoots

// while creating panel allow the path you want to add
// in my case adding media as rootDir to localResources
let panel = vscode.window.createWebviewPanel("webview", "Web View", {  
       viewColumn: vscode.ViewColumn.One
},{    localResourceRoots: [vscode.Uri.joinPath(context.extensionUri, "media")]
     })

下一步是media在扩展文件夹内的根目录中创建一个名为的文件夹。

在该文件夹中,您可以放置​​所有样式文件,例如图像、svg、css 文件等。

vscode.css在媒体文件夹中创建一个文件(您可以命名任何名称)。添加以下内容

:root {
  --container-paddding: 20px;
  --input-padding-vertical: 6px;
  --input-padding-horizontal: 4px;
  --input-margin-vertical: 4px;
  --input-margin-horizontal: 0;
}

html {
  box-sizing: border-box;
  font-size: 13px;
}

*,
*:before,
*:after {
  box-sizing: inherit;
}

body,
h1,
h2,
h3,
h4,
h5,
h6,
p,
ol,
ul {
  margin: 0;
  padding: 0;
  font-weight: normal;
}

img {
  max-width: 100%;
  height: auto;
}

body {
  padding: 0 var(--container-paddding);
  color: var(--vscode-foreground);
  font-size: var(--vscode-font-size);
  font-weight: var(--vscode-font-weight);
  font-family: var(--vscode-font-family);
  background-color: var(--vscode-editor-background);
}

ol,
ul {
  padding-left: var(--container-paddding);
}

body > *,
form > * {
  margin-block-start: var(--input-margin-vertical);
  margin-block-end: var(--input-margin-vertical);
}

*:focus {
  outline-color: var(--vscode-focusBorder) !important;
}

a {
  color: var(--vscode-textLink-foreground);
}

a:hover,
a:active {
  color: var(--vscode-textLink-activeForeground);
}

code {
  font-size: var(--vscode-editor-font-size);
  font-family: var(--vscode-editor-font-family);
}

button {
  border: none;
  padding: var(--input-padding-vertical) var(--input-padding-horizontal);
  width: 100%;
  text-align: center;
  outline: 1px solid transparent;
  outline-offset: 2px !important;
  color: var(--vscode-button-foreground);
  background: var(--vscode-button-background);
}

button:hover {
  cursor: pointer;
  background: var(--vscode-button-hoverBackground);
}

button:focus {
  outline-color: var(--vscode-focusBorder);
}

button.secondary {
  color: var(--vscode-button-secondaryForeground);
  background: var(--vscode-button-secondaryBackground);
}

button.secondary:hover {
  background: var(--vscode-button-secondaryHoverBackground);
}

input:not([type="checkbox"]),
textarea {
  display: block;
  width: 100%;
  border: none;
  font-family: var(--vscode-font-family);
  padding: var(--input-padding-vertical) var(--input-padding-horizontal);
  color: var(--vscode-input-foreground);
  outline-color: var(--vscode-input-border);
  background-color: var(--vscode-input-background);
}

input::placeholder,
textarea::placeholder {
  color: var(--vscode-input-placeholderForeground);
}

.container {
  display: flex;
}

.form {
  display: flex;
  flex-direction: column;
  flex: 1;
  padding: 10px;
  gap: 10px;
}

上面的样式将帮助您将 html 元素的默认样式属性重置为看起来像 vscode 的本机样式。上面提到的变量是由vscode直接提供的。

您还可以添加您选择的任何图像,只需将图像文件放入媒体文件夹中即可。

要应用 css 样式表和图像,只需添加此行并应用于 html

const cssStyle = panel.webview.asWebviewUri(vscode.Uri.joinPath(context.extensionUri, "media", "vscode.css"))

const imgSrc = panel.webview.asWebviewUri(vscode.Uri.joinPath(context.extensionUri, "media", "vim.svg"))

panel.webview.html = `<!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <link rel="stylesheet" type="text/css" href="${cssStyle}" />
    </head>
    <body>
    <div class="container">
        <img src="${imgSrc}" width="200" />
        <div class="form">
            <code>Title</code>
            <input />
            <code>Code</code>
            <textarea></textarea>
            <button>Submit</button>
        </div>
    </div>
    </body>
    </html>`

快速提示:要查看 html 中的更改,您需要重新加载整个扩展,Ctrl + R以快速重新加载,无需每次都重新启动Ctrl + F5

预期输出:

图片[4]-如何使用 React、TypeScript、Tailwind 创建 VS Code 扩展-哈德森博客

脚本编写

您还可以使用 javascript 更新扩展内的 DOM。但需要先启用它。

let panel = vscode.window.createWebviewPanel("webview", "Web View", {
            viewColumn: vscode.ViewColumn.One
        },{
        enableScripts: true
    })

const scriptPath= panel.webview.asWebviewUri(vscode.Uri.joinPath(context.extensionUri, "media", "script.js"))

panel.webview.html = `<!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <script src="${scripts}"></script>
    </head>
    <body>
    <h1>Count:</h1>
        <p id="count">0</p>
    <button onclick="changeHeading()">Add</button>
    </body>
    </html>`

现在,在文件夹script.js内创建文件media,并添加以下行。

//script.js
function changeHeading() {
    document.getElementById("count").textContent = +document.getElementById("count").textContent + 1
} 

运行扩展后的预期输出动图

图片[5]-如何使用 React、TypeScript、Tailwind 创建 VS Code 扩展-哈德森博客

有趣的事实:您无需重新加载即可查看脚本部分的更改,只需关闭并重新打开 Web 视图即可。


实施 React、TypeScript、Tailwind

在了解了有关扩展和webviews的大量知识之后,是时候了解框架了,因为显而易见的原因是它们在使用不同的库时使事情变得更容易。

从 React 开始,您可以选择您选择的任何框架。

Step 1初始化扩展yo code

yo code

? What type of extension do you want to create? New Extension (TypeScript)
? Whats the name of your extension? react-ext
? Whats the identifier of your extension? react-ext
? Whats the description of your extension? LEAVE BLANK
? Initialize a git repository? Yes
? Bundle the source code with webpack? No
? Which package manager to use? npm

? Do you want to open the new folder with Visual Studio Code? Open with `code`

Step 2在根目录中使用typescript创建一个React应用程序。

npx create-react-app web --template typescript
cd web

Step 3在react app目录中web,设置tailwindcss

npm i -D tailwindcss postcss
npx tailwindcss init

编辑tailwind.config.js

//tailwind.config.js
module.exports = {
  content: ['./src/**/*.{js,jsx,ts,tsx}'],
  theme: {
    extend: {},
  },
  plugins: [],
}

编辑web/src/index.css

@tailwind base;
@tailwind components;
@tailwind utilities;

body {
  @apply p-0;
}

Step 4在 React 目录中,创建名为的文件.postcssrc

//.postcssrc
{
  plugins: {
    tailwindcss: { },
  },
}

Step 5编辑反应应用程序web/src/App.tsx

function App() {
  return (
    <div className="bg-gradient-to-r from-blue-600 to-purple-500 p-10">
      <p className="text-white/80 text-xl font-semibold">
        Lorem ipsum dolor sit amet consectetur adipisicing elit. Ea, explicabo
        doloremque deserunt, voluptates, fugiat dolorem consectetur odio autem
        quas ipsa veniam ducimus necessitatibus exercitationem numquam assumenda
        natus beatae sed velit!
      </p>
    </div>
  );
}

export default App;

Step 6安装Parcel,最轻量级的打包器(老实说,在尝试了 webpack 、 babel 和 rollup 之后,我觉得浪费时间)

Parcel作为开发依赖项安装在React根目录中web

npm i -D parcel

在 React 应用程序内部,编辑package.json

//web/package.json
{
...,
"source": "src/index.tsx",
"scripts": {
    "start": "parcel", //overwrite 
    "build": "parcel build", //overwrite
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
}

运行包裹 (Parcelling)

npm start

#> web@0.1.0 start
#> parcel

#Server running at http://localhost:1234

每次您更改内部或与likesrc相关的内容时,它都会重新运行快速构建并将其存储在React 应用程序目录内的文件夹中,如和。src/index.tsxindex.cssdistindex.jsindex.css

Step 7现在进入扩展集成部分,编辑src/extension.ts

import * as vscode from 'vscode';

export function activate(context: vscode.ExtensionContext) {

    let webview = vscode.commands.registerCommand('react-ext.namasteworld', () => {

        let panel = vscode.window.createWebviewPanel("webview", "React", vscode.ViewColumn.One, {
            enableScripts: true
        })

        // web is for my react root directory, rename for yours

        let scriptSrc = panel.webview.asWebviewUri(vscode.Uri.joinPath(context.extensionUri, "web", "dist", "index.js"))

        let cssSrc = panel.webview.asWebviewUri(vscode.Uri.joinPath(context.extensionUri, "web", "dist", "index.css"))

        panel.webview.html = `<!DOCTYPE html>
        <html lang="en">
          <head>
            <link rel="stylesheet" href="${cssSrc}" />
          </head>
          <body>
            <noscript>You need to enable JavaScript to run this app.</noscript>
            <div id="root"></div>
            <script src="${scriptSrc}"></script>
          </body>
        </html>
        `
    });

    context.subscriptions.push(webview);
}

export function deactivate() { }

最重要的过程!
不要忘记在 package.json 中手动写入所有注册的命令,这将导致命令面板中缺少 UI 选项Ctrl+Shift+P

为此,请转到扩展根目录的 package.json

{
...,
"contributes": {
    "commands": [
      {
        "command": "react-ext.namasteworld",
        "title": "React Web View"
      }
    ]
  },
}

Final Step现在,到了关键时刻

  1. 只需按,就会打开Ctrl F5另一个扩展开发。
  2. 在另一个测试 vscode 编辑器Extension Development上,按按打开命令面板Ctrl+Shift+P
  3. 搜索您注册的命令,您在 中给出的名称package.json,在我的例子中是React Web View

并且必须弹出类似的窗口,如下所示

图片[6]-如何使用 React、TypeScript、Tailwind 创建 VS Code 扩展-哈德森博客

预期步骤:

图片[7]-如何使用 React、TypeScript、Tailwind 创建 VS Code 扩展-哈德森博客

有趣的事实:在 React 应用程序中进行更改后,您不需要重新运行,或者App.tsx重新打开窗口,如下所示

图片[8]-如何使用 React、TypeScript、Tailwind 创建 VS Code 扩展-哈德森博客

奖励:VS Code UI 工具包 (React)

使用框架而不是原始 html 的优点,您可以使用 shadcn ui、framermotion 等 UI 库

一个这样有用的例子是我们自己开发的Webview UI Toolkit

这是一个令人惊叹的 ui 库,它使您制作的扩展感觉就像它们的原生扩展一样。

要实现你只需要安装vscode/webview-ui-toolkit在你的反应应用程序中

npm install --save @vscode/webview-ui-toolkit 

编辑App.tsx

import {
  VSCodeButton,
  VSCodeDataGrid,
  VSCodeDataGridRow,
  VSCodeDataGridCell,
  VSCodeTextField,
  VSCodeProgressRing,
} from "@vscode/webview-ui-toolkit/react";

function App() {
  const rowData = [
    {
      cell1: "Cell Data",
      cell2: "Cell Data",
      cell3: "Cell Data",
      cell4: "Cell Data",
    },
    {
      cell1: "Cell Data",
      cell2: "Cell Data",
      cell3: "Cell Data",
      cell4: "Cell Data",
    },
    {
      cell1: "Cell Data",
      cell2: "Cell Data",
      cell3: "Cell Data",
      cell4: "Cell Data",
    },
  ];

  return (
    <div className="grid gap-3 p-2 place-items-start">
      <VSCodeDataGrid>
        <VSCodeDataGridRow row-type="header">
          <VSCodeDataGridCell cell-type="columnheader" grid-column="1">
            A Custom Header Title
          </VSCodeDataGridCell>
          <VSCodeDataGridCell cell-type="columnheader" grid-column="2">
            Another Custom Title
          </VSCodeDataGridCell>
          <VSCodeDataGridCell cell-type="columnheader" grid-column="3">
            Title Is Custom
          </VSCodeDataGridCell>
          <VSCodeDataGridCell cell-type="columnheader" grid-column="4">
            Custom Title
          </VSCodeDataGridCell>
        </VSCodeDataGridRow>
        {rowData.map((row) => (
          <VSCodeDataGridRow>
            <VSCodeDataGridCell grid-column="1">{row.cell1}</VSCodeDataGridCell>
            <VSCodeDataGridCell grid-column="2">{row.cell2}</VSCodeDataGridCell>
            <VSCodeDataGridCell grid-column="3">{row.cell3}</VSCodeDataGridCell>
            <VSCodeDataGridCell grid-column="4">{row.cell4}</VSCodeDataGridCell>
          </VSCodeDataGridRow>
        ))}
      </VSCodeDataGrid>

      <span className="flex gap-3">
        <VSCodeProgressRing />
        <VSCodeTextField />
        <VSCodeButton>Add</VSCodeButton>
        <VSCodeButton appearance="secondary">Remove</VSCodeButton>
      </span>
    </div>
  );
}

export default App;

这个库最好的部分是,它适应 vscode 中的每个可用主题,使用它们的 css 变量(我猜),这是惊人的。

这是一个示例动图

图片[9]-如何使用 React、TypeScript、Tailwind 创建 VS Code 扩展-哈德森博客
我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=5vbhp91f157x
THE END
喜欢就支持一下吧
点赞7 分享
评论 抢沙发

请登录后发表评论

    请登录后查看评论内容