一起来写 VS Code 插件:VS Code 版 CNode 已上线

sxkk20081年前知识分享107

前言

本篇是 VS Code 插件开发实战系列第三篇,前面两篇是

  1. 《一起来写 VS Code 插件:为你的团队提供常用代码片段》
  2. 《一起来写 VS Code 插件:实现一个翻译插件》

CNode  社区为国内最专业的 Node.js 开源技术社区,致力于 Node.js 的技术研究。本篇将通过实现 VS Code 版 CNode, 来带领大家一起熟悉 VSCode Webview 强大的功能。在开始之前,我们先参考 官网关于 webview 的介绍。Webview API 允许扩展在 visualstudio 代码中创建完全可定制的视图,可以将 webview 看作是 VS Code 中的 iframe。

我们可以通过网页将事件消息传递给我们的服务端(包括 NodeJS), 服务端处理完后可以把消息数据传递给网页。因此我们能在 extensions 中开发出跟网页一样的内容,但实现远比网页更强大的功能。

效果

首先来看下实现的效果

nn.gif.gif

主要分为 2 部分,左侧是主题列表,右侧是主题详情。

初始化项目

首先通过脚手架初始化一个 typescript + webpack 的工程

image.png

配置左侧导航图标

 "icon": "icon.png",
  "activationEvents": [
    "onView:vs-sidebar-view"
  ],
  "contributes": {
    "viewsContainers": {
      "activitybar": [
        {
          "id": "vs-sidebar-view",
          "title": "CNODE 社区",
          "icon": "media/cnode_icon_64.png"
        }
      ]
    },
    "views": {
      "vs-sidebar-view": [
        {
          "type": "webview",
          "id": "vs-sidebar-view",
          "name": "Topic 列表",
          "icon": "media/cnode_icon_64.png",
          "contextualTitle": "Topic 列表"
        }
      ]
    }
  },
  ...

views 是配置视图列表,activitybar 是定义下显示在侧边导航上的视图。

注册一个侧边栏

在 extension.ts 中注册一个 与 package.json 对应的 vs-sidebar-view侧边栏 ID

import * as vscode from 'vscode'
import { SidebarProvider } from './SidebarProvider'

export function activate(context: vscode.ExtensionContext) {
  const sidebarPanel = new SidebarProvider(context.extensionUri)
  context.subscriptions.push(
    vscode.window.registerWebviewViewProvider('vs-sidebar-view', sidebarPanel)
  )
}

实现侧边栏

import * as vscode from "vscode";
import { getNonce } from "./getNonce";

export class SidebarProvider implements vscode.WebviewViewProvider {
  _view?: vscode.WebviewView;
  _doc?: vscode.TextDocument;
  constructor(private readonly _extensionUri: vscode.Uri) {}

  public resolveWebviewView(webviewView: vscode.WebviewView) {
    this._view = webviewView;

    webviewView.webview.options = {
      // 在 webview 允许脚本
      enableScripts: true,
      localResourceRoots: [this._extensionUri],
    };

    webviewView.webview.html = this._getHtmlForWebview(webviewView.webview);
  }

  public revive(panel: vscode.WebviewView) {
    this._view = panel;
  }

  private _getHtmlForWebview(webview: vscode.Webview) {
    const scriptUri = webview.asWebviewUri(
      vscode.Uri.joinPath(this._extensionUri, "build", "static/js/main.js")
    );
    const styleMainUri = webview.asWebviewUri(
      vscode.Uri.joinPath(this._extensionUri, "build", "main.css")
    );

    // Use a nonce to 只允许特定脚本运行.
    const nonce = getNonce();

    return `
		
		
		
                
		
		
                
                
              
                  
`; } }

上述代码采用面向对象的方式实现一个 SidebarProvider类,根据 vscode.WebviewViewProvider, 其实实现所有的 WebviewViewProvider 都是是这段代码,其他代码都是相同的,因为关于 webview 中的 HTML 我们都可以使用 js 来生成,这不正是我们的单页面应用开发吗?

上述代码中, Nonce是一个在加密通信只能使用一次的数字。在认证协议中,它往往是一个随机或伪随机数,以避免重放攻击。Nonce 也用于流密码以确保安全。如果需要使用相同的密钥加密一个以上的消息,就需要 Nonce 来确保不同的消息与该密钥加密的密钥流不同。 所以我们直接拷贝官方 demo 中的代码。

//  生成特定随机数
export function getNonce() {
  let text = ''
  const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
  for (let i = 0; i < 32; i++) {
    text += possible.charAt(Math.floor(Math.random() * possible.length))
  }
  return text
}

实现侧边视图

CNode 提供了允许跨域的 API,我们可以在 js 中直接调用,如果你也想开发类似的功能请在 HTTP headers 中加入

Access-Control-Allow-Origin: *

配置 webpack config

在原先 webpack.config.js 中加入打包 React 的没配置,webpack5 支持多份 config 配置。

const viewConfig = {
  entry: './view/index.tsx',
  output: {
    path: path.resolve(__dirname, 'build'),
    filename: 'static/js/[name].js',
  },
  mode: 'production',
  plugins: [new miniCssExtractPlugin()],
  module: {
    rules: [
      {
        test: /\.(ts|tsx)$/i,
        loader: 'ts-loader',
        exclude: ['/node_modules/'],
      },
      {
        test: /\.css$/i,
        use: [miniCssExtractPlugin.loader, 'css-loader', 'postcss-loader'],
      },
    ],
  },
  resolve: {
    extensions: ['.ts', '.tsx'],
  },
}

module.exports = [extensionConfig, viewConfig]

然后启动调试的时候,webpack 就会自动打包了;

注意 这里的 mode 必须设置为 production,webpack development 模式会使用 eval 来执行代码,而 eval 在 VS Code webview 不允许执行。

配置 tailwindcss

为了方便,我这边使用了 tailwindcss,因为我可以使用 tailwindcss-typography 这个插件,帮我生成漂亮的文章类型排版。

yarn add tailwindcss @tailwindcss/typography autoprefixer

使用命令初始化 tailwindcss config

npx tailwindcss init
module.exports = {
  mode: 'jit',
  purge: ['./view/**/*.tsx'],
  darkMode: false, // or 'media' or 'class'
  theme: {
    extend: {},
  },
  variants: {
    extend: {},
  },
  plugins: [require('@tailwindcss/typography')],
}

mode jit 是及时编译模式 tailwindcss 2.1 版本加的,忽略掉我们不需要的 css 代码。

生成文章页面的样式

.markdown-preview {
  @apply prose prose-lg max-w-full bg-white p-20;
}

使用 React 来实现主题列表

使用 react 实现一个列表的代码我这边就不叙述了,跟我们平常写业务没什么区别,最主要的是 数据通信,当我们点击主题列表,右边要打开一个新的 webview 页面

const handleClick = (item: Topic) => {
  setCurrent(item.id)

  tsvscode.postMessage({ type: 'detail', value: item })
}

根据官方例子

scripts-webview_to_extension.gif

Webviews 还可以将消息传递回它们的扩展。这是通过在 webview 中的特殊 VS Code API 对象上使用 postMessage 函数来实现的。要访问 VS Code API 对象,就要在 webview 中调用 acquireVsCodeApi函数。

定义 TS 全局对象

import * as _vscode from "vscode";

declare global {
  const tsvscode: {
    postMessage: ({ type: string, value: any }) => void;
    getState: () => any;
    setState: (state: any) => void;
  };
  const apiBaseUrl: string;
}

接着就可以在 vs-sidebar-view 接收数据了

webviewView.webview.onDidReceiveMessage(async (data) => {
  switch (data.type) {
    case 'detail':
      createPreviewPanel(this._extensionUri, data.value)
      break

    default:
      break
  }
})

实收到数据后可以就可以打开一个预览页面了

预览页面实现

function createPreviewPanel(topic: Topic) {
  // 创建一个新的 panel.
  const panel = vscode.window.createWebviewPanel(
    'cnode-preview',
    'CNODE 技术社区',
    column || vscode.ViewColumn.One,
    {
      // 在 webview 允许脚本
      enableScripts: true,

      // 限制 从 media 文件夹加载资源
      localResourceRoots: [vscode.Uri.joinPath(extensionUri, 'build')],
    }
  )

  panel.webview.html = _getHtmlForWebview(panel.webview, topic)
}

可以使用 内置方法 vscode.window.createWebviewPanel 创建一个新的面板,并且接收主题数据。 _getHtmlForWebview 与 SidebarProvider 中的 _getHtmlForWebview 一致,返回 HTML 即可。

避免重复创建预览页

当然也可以通过 postMessage 传递属性

extension 端

panel.webview.postMessage({text: 'hello'});

webview 端

window.addEventListener('message', event => {
    const message = event.data;
    console.log('Webview接收到的消息:', message);
}

主题适配

VS Code 将主题分为三类,并在 body 元素中添加一个 class 来指示当前主题:

body.vscode-light {
  color: black;
}

body.vscode-dark {
  color: white;
}

body.vscode-high-contrast {
  color: red;
}

Webviews 还可以使用 CSS 变量访问 VS Code 主题颜色。这些变量名以 vscode 作为前缀,并用-替换.。例如 editor.foreground 变为 var (--vscode-editor-foreground)。

查看可用主题变量的主题颜色参考。还有一个扩展可以为变量提供智能建议。

调试

要调试 Webview 不能直接把 VSCode 的开发者工具打开,直接打开你只能看到一个标签,看不到代码,要看代码需要按下Ctrl+Shift+P然后执行打开Webview开发工具,英文版应该是Open Webview Developer Tools

image.png

从上图也可以看的 在 html 标签上注入了当前皮肤的 css 变量。

状态保持

与浏览器标签不一样的是,当 webview 移动到后台又再次显示时,webview 中的任何状态都将丢失。因为 webview 是基于 iframe 实现的。

解决此问题的最佳方法是使你的 webview 无状态,通过消息传递来保存 webview 的状态。

 state

在 webview 的 js 中我们可以使用vscode.getState()vscode.setState()方法来保存和恢复 JSON 可序列化状态对象。当 webview 被隐藏时,即使 webview 内容本身被破坏,这些状态仍然会保存。当然了,当 webview 被销毁时,状态将被销毁。

序列化

通过注册WebviewPanelSerializer可以实现在VScode重启后自动恢复你的webview,当然,序列化其实也是建立在getStatesetState之上的。

注册方法:vscode.window.registerWebviewPanelSerializer

 retainContextWhenHidden

对于具有非常复杂的 UI 或状态且无法快速保存和恢复的webview,我们可以直接使用retainContextWhenHidden选项。设置retainContextWhenHidden: true后即使 webview 被隐藏到后台其状态也不会丢失。

尽管retainContextWhenHidden很有吸引力,但它需要很高的内存开销,一般建议在实在没办法的时候才启用。

getStatesetState是持久化的首选方式,因为它们的性能开销要比retainContextWhenHidden低得多。

发布

关于发布可以看我的上一篇 一起来写 VS Code 插件:为你的团队提供常用代码片段

小结

本篇通过实现 VS Code 版 CNode 来帮我们熟悉 webview 的 api,当然还可以增加评论系统,创建主题,基于用户系统可以实现点赞收藏等。

开发更复杂的功能,只缺你的想象力。例如:

  1. 韭菜盒子,做最好用的股票和基金插件

  2. create-app 可视化 CLI 工具

最后

附上本插件的下载地址源码

同时 vscode extensions 开发门槛不高,欢迎大家尝试,或者将有意思的 extensions 推荐在评论区。

希望这篇文章对大家有所帮助,也可以参考我往期的文章或者在评论区交流你的想法和心得,欢迎一起探索前端。

相关文章

用AI智能识别技术助力未来发展

用AI智能识别技术助力未来发展

  随着人工智能技术的飞速发展,AI智能识别正成为推动社会进步的重要力量。它通过模拟人类的认知过程,实现机器对图像、语音和文字的理解与处理,为各行各业带来了前所未有的创新。 ...

AI技术广泛应用:从医疗到金融的探索与挑战

AI技术广泛应用:从医疗到金融的探索与挑战

  近年来,随着技术的不断进步,人工智能(AI)逐渐实现了从科幻梦想到现实应用的转变。尤其是在医疗、金融等领域,AI技术正在广泛应用,为人类创造了更多的可持续发展和公平共享的...

小忆机器人:让智能陪伴生活的新选择

小忆机器人:让智能陪伴生活的新选择

  在如今快节奏的生活中,人们对于智能机器人的需求越来越多。小忆机器人作为一款新型智能陪伴机器人,以其独特的功能和灵动的外观,成为了人们追寻智能生活方式的新选择。  小忆机器...

2-3 分钟后会在屏幕上输出宝塔面板的登录地址,宝塔面板会随机生成用户名、密码和端口,我们需要把这些地址保存到本地,以免下次忘记。

接下来我们需要在云服务器上设置安全组或者防火墙,放行自动生成的端口。

腾讯云配置安全组

开通了端口,输入宝塔面板地址,输入用户名和密码就可以登录了 宝塔面板登录

域名解析

腾讯云域名解析

在你的域名服务商后台,将一个域名解析到 这台这台服务器 ip,解析完成后,你就可以使用域名访问了。

安装 PHP+mysql

登录后要先绑定一个宝塔账号,这个大家自行注册就可以了 选择系统推荐的 LNMP 环境 绑定成功后,我们来安装 PHP+mysql,系统会自动弹窗框让我们来选择环境,我这里选择 LNMP

  • 急速安装,安装时间极快(5-10 分钟),版本与稳定性略低于编译安装,适合快速部署测试
  • 编译安装,安装时间长(30 分钟到 2 小时),性能最大化,适合生产环境, 点击一键安装后,宝塔面板就会开始安装环境了

LNMP 环境安装进度 等待 10 分钟后,环境安装完成,当然有经验的同学可以自行安装 PHP+mysql 的环境,但是使用宝塔面板对新手比较友好。

访问IP显示会宝塔404页面 此时访问我们的 IP 就可以看到页面,说明我们的环境已经安装成功了。

安装微擎

微擎官网:https://www.we7.cc/

微擎官方文档:https://www.kancloud.cn/donknap/we7/136557

微擎源码地址:https://gitee.com/we7coreteam/pros

微擎是一款小程序和公众号管理系统,可以实现微信平台(mp.weixin.qq.com)不能实现的功能,例如商城,餐饮,酒店,汽车,门店,同城,各类行业解决方案,营销,推广,吸粉,游戏,物联网和人工智能等功能,这些应用大部分收费,也有免费的应用,大家可以在官网上搜索安装。

点击网站,添加网站,输入你自己的要解析的域名, 宝塔面板添加网站

选择创建数据库,和 FTP,点击提交,此时输入我们的域名可以看到如下页面

宝塔默认创建的页面

说明我们的网站创建成功了。

安装微擎框架

在网站 ftp 目录下上传微擎框架的源码 上传微擎框架源码

上传完成后点击 zip 文件解压

设置网站默认站点

点击默认站点,设置我们刚才创建的网站

输入IP,开始安装微擎 输入 IP 地址,就可以进入微擎的安装页面了。 微擎安装页面检查环境

安装过程中会检查 PHP 环境要求,若检查不成功,我们需要修改相应的 PHP info 文件。

微擎配置数据库和密码

点击继续输入刚才创建的数据库信息,并且设置微擎后台密码

微擎安装完成

点击继续安装完成

《成语小秀才》部署教程

前言相信很多朋友都玩过这款小游戏,叫成语秀才,没体验过的朋友可以点击这里,扫码体验,这款小游戏开发起来还是比较困难的,首先要有一份海量的题库,然后在每道题都有不同的布局,我是一个非常喜欢学习的人,于是...

AI技术的发展与应用-探索人工智能的新未来

AI技术的发展与应用-探索人工智能的新未来

   人工智能(AI)在20世纪50年代开始被提出,如今在技术和应用上取得了巨大的进步,得到了各行各业的应用。随着数据和算力的爆发式增长,我们正在看到AI技术的未来发展将更加...

人工智能改变生活,预测未来生活将变成什么样?

人工智能改变生活,预测未来生活将变成什么样?

  人工智能(AI)作为一种新兴技术,正在改变着我们的生活方式。随着科技的不断发展,越来越多的人工智能应用进入到我们的日常生活中,其中包括我们生活中的网购、社交、出行、医疗甚...

发表评论    

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。