当前位置:   article > 正文

前端工程化-VSCode插件集成脚手架和组件库

visual studio code 脚手架

 a12b5f22dfecdbac1e95d797dd3c2b69.gif

本文字数:14404

预计阅读时间:37 分钟

目录

  • VSCode插件能做什么?

  • VSCode可扩展能力有哪些?

  • 如何开发一个VSCode插件?

  • VSCode插件如何集成基建的脚手架和组件库?(FAW保姆级教程)

  • 前端常见插件的实现原理分析?


前言

我们程序员每天的产出大部分都是在IDE中完成,大家在日常开发过程中,多多少少会有些自己的特殊定制需求去提升开发效率,比如写shell脚本、浏览器插件等,在Visual Studio Code (VSCode)中我们也能开发一些插件去满足日常工作需要。

比如现在业务要新开发一个项目,设计稿风格和之前系统类似。那我第一想法肯定是去拷贝已有项目的代码(或者使用组内抽象的模板),然后稍作修改满足当前需求。但如果是新同学往往需要经历咨询已有项目/模板相关人员->开通各种权限->复用部分代码并做个性化修改->借助组件库、工具库进入业务功能开发,这个过程有一定沟通和时间成本。

所以我期望能有一个更直观的方式让新同学了解组内有哪些基建并投入使用,比如能直接在VSCode中罗列当前的模板项目,预览后选择特定模板进行项目初始化,并且将一些个性化基础配置通过表单形式进行填写并渲染,避免遗漏。而且在开发过程能在VSCode中直观的展示当前有哪些组件和工具函数可以使用,然后通过点点点操作实现组件的添加和快速使用。

本文也将带着下面几个问题去讲解开发VSCode插件的过程:

  • VSCode插件能做什么?

  • 如何开发一款VSCode插件?

  • VSCode中如何嵌入webview

  • VSCode中如何配置国际化?

  • VSCode插件中如何新建项目、新建页面、组件...?

VSCode插件能做什么

目前常用 VSCode 插件可分为下面几大类:

  • 语言类插件

    • 语法高亮(Vetur)

    • 代码自动补全(TabNine)

    • 代码片段(JS JSX Snippets)

  • 工具类插件

    • 可视化搭建页面(面向开发者的低代码)(AppWorks)

    • 时间管理(WakaTime)

    • Git管理(Git Graph)

    • TODO(TODO Tree)

  • 娱乐类插件

    • 听音乐(VSC Netease Music)

    • 炒股(韭菜盒子)

    • 玩游戏(小霸王)


VSCode可扩展能力

本章大部分内容在官网中已有说明,此次做简单了解

VSCode 提供哪些能力去实现上一章所提到的效果?


基于Electron能力

VSCode本身是使用Electron开发的,那他也支持对应的能力。

  • 支持读取本地文件

  • 支持发送接受跨域请求

  • 支持创建本地服务器

  • 持久化存储本地数据

可扩展能力

  • 使用颜色或文件图标主题更改

    VSCode

    外观

  • UI

    中添加自定义组件和视图

  • 创建

    webview

    以显示使用

    HTML/CSS/JS

    构建的自定义网页

  • 支持一种新的编程语言

  • 支持调试特定运行时


扩展工作台

9bba95e75ddded1c5e22543b17f43c2a.png

VSCode提供了各种 API,允许您将自己的组件添加到工作台。

  • 活动栏(Tree View Container):Azure 应用服务扩展添加了一个视图容器

  • 侧边栏(Tree View):内置的 NPM 扩展在 Explorer 视图中添加了一个树视图

  • 编辑器组(Webview):内置的 Markdown 扩展在编辑器组中的其他编辑器旁边添加了一个 Webview

  • 状态栏(Status Bar):

    VSCodeVim

    扩展在状态栏中添加了一个状态栏项


扩展编辑器

  • 基于正则编辑页面中的内容

    • 例如:删掉当前页面所有注释或

      log

  • 自定义跳转、自动补全、悬浮提示

    • 例如:输入

      rfc

      自动补全代码

  • 对特定后缀名文件的解析和编辑

    • 例如:借助插件

      vetur

      解析

      .vue

      文件

  • 增强

    VSCode

    内置的

    MD

    预览和

    Git

    工具

    • 例如:美化预览

      .md

      文件


限制

与此同时,也存在一些限制,比如插件不能访 问 VSCode UI 的 DOM 节点。(如果强行改动,VSCode 会提示自身损坏)

开发插件

首先对VSCode插件能力有个大概认识,然后从HelloWorld初始化项目去入门,再去集成Webview。

由于VSCode本身是使用Electron开发的,且Electron是基于Chromium,渲染进程是使用Web页面作为 UI 显示。那在VSCode中也能集成webview。

初始化项目

npm i -g yo generator-code

借助官方提供的脚手架生成项目

  1. yo code
  2. ? What type of extension do you want to create? New Extension (TypeScript)
  3. ? What's the name of your extension? HelloWorld
  4. ## Press <Enter> to choose default for all options below ###
  5. ? What's the identifier of your extension? helloworld
  6. ? What's the description of your extension? LEAVE BLANK
  7. ? Initialize a git repository? Yes
  8. ? Bundle the source code with webpack? No
  9. ? Which package manager to use? npm
  10. ? Do you want to open the new folder with Visual Studio Code? Open with `code`

页面关键结构如下:

  1. .
  2. ├── package.json # 插件配置
  3. ├── src
  4. │   ├── extension.ts # 入口文件
  5. ├── tsconfig.json

package.json关键内容如下:

  1. {
  2.   // 扩展的激活事件
  3.   "activationEvents": [
  4.     "onCommand:extension.sayHello"
  5.   ],
  6.   // 入口文件
  7.   "main""./src/extension",
  8.   // 贡献点,vscode插件大部分功能配置都在这里
  9.   "contributes": {
  10.     "commands": [
  11.       {
  12.         "command""extension.sayHello",
  13.         "title""Hello World"
  14.       }
  15.     ]
  16.   }
  17. }

src/extension.ts关键内容如下:

  1. const vscode = require('vscode');
  2. // 插件被激活时触发,所有代码总入口
  3. exports.activate = function(context) {
  4.   // 注册命令 与`package.json`中`contributes.commands`
  5.   context.subscriptions.push(vscode.commands.registerCommand('extension.sayHello', function () {
  6.     vscode.window.showInformationMessage('Hello World!');
  7.   }));
  8. };
  9. // 插件被释放时触发
  10. exports.deactivate = function() {};

然后在编辑器中按F5 即可打开新的窗口在命令面板中(⌘⇧P)运行 Hello World 命令进行调试插件:

6731069617d19548eb5ab9f798a0a4c4.gif


集成Webview

下方示例为在VSCode集成通过ice生成的webview

1.创建web目录初始化项目

  1. mkdir web
  2. cd web
  3. yarn create ice
  4. # or
  5. yarn create @umijs/umi-app

2.配置package.json注册激活事件

  1. {
  2.   "activationEvents": ["onCommand:project-creator.create-project.start"],
  3.   "contributes": {
  4.     "commands": {
  5.       "command""project-creator.create-project.start",
  6.       "title""创建项目webview"
  7.     }
  8.   }
  9. }
  • activationEvents字段值为数组,通过onCommand注册激活事件project-creator.create-project.start,而project-creator.create-project.start将在contributes.commands中定义

  • contributes字段可以配置扩展VSCode各种能力,比如commands命令configuration配置...

      • commands中的command将在src/extension.ts中进行注册事件回调

3.配置src/extension.ts创建webview的具体逻辑

  • 注册命令project-creator.create-project.start

  • 创建

    webview

    面板

    projectCreatorWebviewPanel

      • 如果有,则直接展示

      • 如果没有,则新建

  • 配置基本配置

      • 标题

      • 启用

        JavaScript

        脚本

      • 隐藏时保留上下文

      • 图标

  • 设置 webview 面板内容

      • 提供

        webview

        vscode

        交互

45c31276d0d7751b82b1fac32b73ed6a.png

VSCode中的Webview本质就是一个iframe,所以是可以在其中执行脚本,但是VSCode默认禁用JavaScript,所以需要配置enableScripts=true开启此功能。

提供Webview内容

通过getHtmlForWebview获取 webview 的内容。

由于使用icejs进行构建项目,yarn build后的目录结构为index.htmlcss/index.cssjs/index.js,如果开启MPA,则还有vendor.css/js

如果使用其他框架比如 umijs,则采取不同的处理方式即可。

a4c1039299f898f73c1346c349e33a2b.png

其中通过getNonce生成一个随机数,设置到scriptnonce属性,作用是在加密通信中使用一次随机数避免重复攻击,保证不同的消息与该秘钥加密的秘钥流不同。此代码拷贝自VSCode提供的官网示例。

Webview和VSCode通信

一个很常见的场景,我们在webview中通过调接口获取数据,然后渲染页面。但是在vscode webview中是不允许发送ajax请求,所有请求都是跨域(因为webview本身没有host),所以需要在VScode中进行真实的接口请求。

此过程则变为在Webview端使用vscode.postMessage,然后在VScode中使用webview.onDidReceiveMessage接收到消息后做相应处理。

将交互过程封装成connectService和callService进行统一注册和调用。

  • 可以在

    VSCode

    端创建

    Webview

    时绑定

    connectService

    ,在其中监听

    webview

    接收到的消息,然后调用

    VSCode

    api

    能力,将执行结果返回给

    Webview

  • Webview

    中调用

    callService

    ,然后将事件和参数传递给

    connectService

    处理,也将处理结果传给回调函数。

10696cdf764163554eafd43793438551.png

e047319941a5b3ca3c0350339abf3cd0.png

在options中提供当前页面需要使用的所有服务services的定义,然后再接收到调用事件时,通过const api = services && services[service] && services[service][method]获取到具体的方法,并将参数进行传递,一定程度去抹平API的差异,减少重复代码量。

8068eebb45586e6d5bd8880b867d06ce.png

国际化

VSCode的国际化主要有三部分组成:

  • 配置项国际化

  • VScode

    代码国际化

  • Webview

    代码国际化

配置项国际化

我们可以在package.json中配置VSCode的配置项,这些配置项的国际化是约定在package.nls.json和package.nls.zh-cn.json这些文件中。

比如可以在package.nls.json中配置插件英文名称:

  1. {
  2.   "projectCreator.create-project.commands.start.title""Select Scaffold to Create Application"
  3. }

在package.nls.zh-cn.json中配置插件中文名称:

  1. {
  2.   "projectCreator.create-project.commands.start.title""选择模板创建应用"
  3. }

然后在package.json中使用:

  1. "contributes": {
  2.   "commands": [
  3.     {
  4.       "command""project-creator.create-project.start",
  5.       "title""%projectCreator.create-project.commands.start.title%"
  6.     }
  7.   ]
  8. }

VScode代码国际化

国际化的解决思路都一样:

2e575234c1330412ee6cf392c900e9ba.png

在代码中进行注册,并且可以通过vscode.env.language获取VSCode当前语言环境。

  1. import * as vscode from 'vscode';
  2. import I18nService from './i18n';
  3. import * as zhCNTextMap from './locales/zh-CN.json'// { "webViewTitle": "Create Project" }
  4. import * as enUSTextMap from './locales/en-US.json'// { "webViewTitle": "创建项目" }
  5. // 注册语言表
  6. const i18n = new I18nService();
  7. i18n.registry('zh-cn', zhCNTextMap);
  8. i18n.registry('en', enUSTextMap);
  9. // 设置使用的语言
  10. i18n.setLocal(vscode.env.language);
  11. export default i18n;

然后在代码中进行使用:

  1. projectCreatorWebviewPanel = vscode.window.createWebviewPanel(
  2.   'project-creator'// webview 标识,只供内部使用
  3.   i18n.format('webViewTitle'), // 标题
  4.   vscode.ViewColumn.One, // 新开一个编辑器视图
  5.   {
  6.     enableScripts: true// 启用 JavaScript 脚本
  7.     retainContextWhenHidden: true// 隐藏时保留上下文
  8.   },
  9. );

Webview代码国际化

Webview中我们采用icejs搭建项目,那就可以使用react-intl来配置国际化。

0bfab19af53906eb6623ad568f293697.png

然后在代码中进行使用:

e6f7e0c2129688fc6bd0d50fb810bdca.png


VSCode插件集成基建

前端同学在开发过程中一般会经历但不限如下过程:

  • 开发准备阶段

    需求评审,查阅外部或组内知识库、开发规范

  • 编码&联调阶段:

    按需求场景根据外部或组内脚手架、组件库、工具库...进行编码调试

  • 调式优化阶段

    数据埋点、性能优化、自动化测试...

  • 构建部署阶段:大部分企业都有自研的devops解决方案

  • 上线后数据采集&分析阶段:

    进行性能监控、报警、数据分析...

  • 技术沉淀:

    对上述过程进行复盘、总结、抽象,进入下一轮需求开发

当我们进入一个新团队时,往往期望能对团队内部的前端研发全链路有一个基本认识,进而可以快速进入开发或投身到感兴趣的技术建设。

当我们开发一个新项目时,往往期望能参考老项目看是否能复用部分,进而减少不必要的重复性工作。

基于上面章节对VSCode插件所提供的能力介绍,我们完全可以将前端研发全链路的基建集成到我们日常编码IDE中,并且提供可视化的操作界面,让我们能安心在IDE中进行开发调试,从一定程度减少我们开发过程到处检索而分心低效的问题。


AppWorks

AppWorks是一款基于VSCode插件的前端研发工具集,通过 GUI 操作、物料组装、代码辅助等功能让前端开发更加简单。

不过由于下面几个原因,我们决定基于AppWorks做个性化改造以便满足部门内部使用。

  • 他对icejsRax类型项目支持友好,但由于我们部门中后台项目技术选型为umijs,在使用AppWorks时面板内容显得有点冗余。

  • 并且我们项目使用

    微前端

    架构,在slave项目中不少配置是期望在初始化模板时就自动配置好。

  • 物料

    方面我们有自己一套组件库并且放在私有npm,自定义物料的方式也期望能保留我们当前发包结构

    • 物料:

      分为组件(component)、区块(block)和项目(scaffold)三种类型

基于上述考虑,我们做了二次开发并产出了FAW,下面将从使用效果去揭秘他的核心逻辑实现。


FAW使用效果

下面示例为新建一个微前端子应用的场景

  • 1. 通过点击侧边栏激活创建项目流程

  • 2. 选择具体模板后点击下一步

  • 3. 输入项目名称、模板版本

  • 4. 如果模板提供ask-for-vscode.js文件,则根据配置生成表单

    • 主要是配置publicPathbasePathmountElementIdid...

  • 5. 表单填写完毕后点击完成

  • 6. 生成项目后在当前窗口打开新项目,即可进入开发

867eab8127911e72a37753cd4cce8cdd.gif


FAW整体架构

根据开发插件章节,可以将模板选择、填写配置这些交互功能放在展示层webview中实现,而将获取模板、拷贝模板并渲染这些功能交由业务层VSCode实现。

于此同时可以在入口AppWorks中“捆绑”组内高频使用插件,实现安装一个插件时可以安装一系列插件。

并且将一些公共配置项、国际化、创建项目和创建物料的核心逻辑...放入packages中使用lerna做管理并在插件中使用。

物料基本信息放在配置平台中做统一配置;项目模板存放在Gitlab做版本管理;组件库放在私有npm做管理。

629f98a2c1b771c6c49553a45dbe49e9.png



FAW新建项目

逻辑类似前端工程化-打造企业通用脚手架-focus create projectName核心流程


核心流程

f0a2a9889debb3e97f11fcc97861d0c0.png

  • 1. 点击“创建应用”,唤起webview页面

  • 2. 从配置中心拉取所有“项目模板”列表

  • 3. 选择“具体模板”后,拉取所有版本(版本默认约定为在Gitlab端打的tag

  • 4. 选择“具体版本”后,判断当前模板是否提供ask-for-vscode.js文件

  • 4.1 如果没提供则对本模板本版本做本地缓存,方便下次使用。则进入第6步

  • 4.2 如果提供则根据配置项渲染为表单供开发者填写

    • 配置项一般为publicPathbasePathmountElementIdid...

  • 5. 通过ncp把代码拷贝到本地临时目录,然后根据 4.2 填写的内容渲染变量在ejs模板,最后通过metalsmith遍历所有文件做插入修改

  • 6. 打开新窗口并启动当前项目

  • 7. 完成,开始进入代码编写

    核心代码实现


核心代码实现

其中第2步定义模板物的结构,然后在配置平台维护一个json存放所有模板。

49b10c7694141f6c8bf26811a91ef0b2.png

第3步中选择具体模板后拉取所有版本,主要借助Gitlab提供的开放能力 https://docs.gitlab.com/ee/api/api_resources.html。

1810f0fd263fb899a247a03628e3c646.png

第4步中选择具体版本后,拉取对应代码,并判断是否存在ask-for-vscode.js文件并解析其内容:

0ad739f56b19c00c061b0bf7713704bd.png

因为require需要以require(/Users/${filename}.js)的形式导入绝对路径+变量,然而我们模板的名字以及配置都名为变量,故获取不到。

  1. // 此方式可行 ✅
  2. const code = require('/Users/careteen/Desktop/admin-umi-template/ask-for-vscode.js')
  3. // 此方式不可行 ❌
  4. const templateName = 'admin-umi-template'
  5. const configName = 'ask-for-vscode'
  6. const args = require(`/Users/careteen/Desktop/${templateName}/${configName}.js`)

所以此处采用readFileSyncnew Function(code)()的方式获取js文件内容。其中内容如下:

  1. // 需要根据用户填写修改的字段
  2. const requiredPrompts = [
  3.   {
  4.     type'input',
  5.     name: 'repoNameEn',
  6.     message: 'please input repo English Name ? (e.g. `smart-phone`.focus.cn)',
  7.   },
  8.   {
  9.     type'input',
  10.     name: 'repoNameEnCamel',
  11.     message: 'please input repo English Camel Name ?(e.g. smart-case.focus.cn/`smartPhone`)',
  12.   },
  13.   {
  14.     type'input',
  15.     name: 'repoNameZh',
  16.     message: 'please input repo Chinese Name ?(e.g. `智能话机`)',
  17.   },
  18. ];
  19. return {
  20.   requiredPrompts,
  21. };

用这部分内容渲染成表单,然后再根据用户输入内容渲染ejs模板,比如配置文件config/config.ts

  1. // 模板 
    声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/568887
    推荐阅读
    相关标签