前端工具链之简单的Vite插件

153 阅读3分钟

0x00 起因

某日上班摸鱼期间,正在了解前端工程化和基建相关内容,公司最近正好在做相关的。无意间看到Vite文档里贴出了awesome-vite这个仓库,是Vite官方收集的社区优秀插件,简单看了一下,发现其中有一个非常简单的插件,作用就是在编译的时候自动在<body>标签下插入腾讯的v-console引入的<script>标签,便于移动端调试。

0x01 疑惑

“这么简单的玩意还能上Vite官方的推荐?”然后,我转给实习公司的mentor看,他给出了“小而美”的评价。我想既然“小而美”,那么用Vite插件解决当下一些工程化或基建上的问题,应该是个不错的选择,毕竟公司的Web脚手架已经全面转向Vite了,做个Vite插件不管对于公司研发效率还是对于个人提升都还是挺有价值的。

0x02 阅读文档

Vite作为尤大的又一个杀手级产品,继承了Vue的优良传统,尤其是优秀的中文文档,看起来非常舒服,很容易就能上手。模仿上面的那个v-console插件,我写了一个快速接入阿里云ARMS监控系统的插件,并根据实际业务需求给出一些配置选项,比如说通过配置限制只能在生产环境开启监控等。正好能解决当下团队的一些需求。

这些功能也并非事先设计好的,也是看到了文档的某些API,受到启发发现可以有这个功能。当然,后面再加功能就得看实际使用中遇到什么问题或需求了。

0x03 实现

项目组织我也仿了那个插件,充分体现了开源在交流学习上的作用😎

index.ts

import type { Plugin } from 'vite'
import { asyncLoader, syncLoader } from './loadMethod'

export interface AliyunARMSPluginOptions {
  /**
   * 是否启动插件,该选项优先级最高
   * 
   * @default true
   */
  enable?: boolean
  /**
   * 插件启动的场景
   * 
   * @see https://cn.vitejs.dev/guide/api-plugin.html#conditional-application
   */
  apply?: Plugin['apply']
  /**
   * 加载方式
   * 
   * @default 'async'
   * 
   * @description npm方式暂不可用
   */
  loadMethod?: 'async' | 'sync'
  /**
   * ARMS实例设置
   * 
   * @see https://help.aliyun.com/document_detail/58655.htm
   */
  configs: Record<string, any>

}



export default function plugin(options: AliyunARMSPluginOptions): Plugin {

  const { enable = true, apply, loadMethod = 'async', configs } = options

  if (!enable) {
    return {
      name: 'vite-plugin-aliyun-arms'
    }
  }

  return {
    name: 'vite-plugin-aliyun-arms',
    enforce: 'pre',
    apply: apply,
    transformIndexHtml(html) {
      switch (loadMethod) {
        case 'async':
          return asyncLoader(configs)
        case 'sync':
          return syncLoader(configs)
        default:
          return html
      }
    }
  }
}

loadMethod.ts

import { HtmlTagDescriptor } from "vite";

export function asyncLoader(configs: any): HtmlTagDescriptor[] {
  return [
    {
      tag: 'script',
      injectTo: 'body-prepend',
      children: `
      !(function (c, b, d, a) {
        c[a] || (c[a] = {})
        c[a].config = ${JSON.stringify(configs)}
        with (b) with (body) with (insertBefore(createElement('script'), firstChild)) setAttribute('crossorigin', '', (src = d))
      })(window, document, 'https://retcode.alicdn.com/retcode/bl.js', '__bl')
      `
    }
  ]
}

export function syncLoader(configs: any): HtmlTagDescriptor[] {
  return [
    {
      tag: 'script',
      injectTo: 'body-prepend',
      children: `
      window.__bl = {
        config: ${JSON.stringify(configs)}
      }
      `
    },
    {
      tag: 'script',
      injectTo: 'body-prepend',
      attrs: {
        type: 'text/javascript',
        crossorigin: true,
        src: 'https://retcode.alicdn.com/retcode/bl.js'
      }
    }
  ]
}

最后,在package文件里把项目标记为module,然后使用tsup打包,导出类型定义、ES Module和Commonjs模块,默认是ESModule,这样可以直接把这个module引入到Vite配置当中和其他插件一起使用了。

Z-Bokle/vite-plugin-aliyun-arms (github.com)

引入方法来自阿里云ARMS官方的接入教程,由于npm包方式接入会导致一些功能不可用,团队暂时不需要,我也就先没做(其实目前也不会做🥲)。

0x04 原理

原理和上面提过很多次的“小而美”v-console插件基本一致,接受外部的配置,根据配置,借助Vite特有的钩子transformIndexHtml,在编译时期往index.html中注入<script>标签,达到自动引入的作用,非常简单。

判断环境用的是“情景应用”的API,通过判断是serve还是build状态区分环境。企业里的项目一般还会分出开发环境、测试环境、生产环境等等多个环境,一般用的是Vite命令来区分,这种方式在“情景应用”中也能支持。 插件 API | Vite 官方中文文档 (vitejs.dev)

0x05 感想

实习期间做点不一样的才能体现出实习的价值,天天写后台管理做crud纯属浪费生命。第一次写Vite插件,非常有意思,也学到了东西,还能收获同事在部门群里的一个个赞,收获颇丰😋。