记一次vite插件开发历程 (vite-plugin-iconify-vue-offline)
其实这个插件已经写了有7个月了, 但是最近公司的项目有遇到类似的问题,所以把插件又做了一个适配修改, 本来觉得没什么, 但是突然心血来潮, 想记录一下, 可能是自己唯一的一篇, 也可能以后突然又是心血来潮会再写一些. 不多说下面进入正题
背景
从事前端也有n年了, 但一直处于较躺平的状态, 平时也只是刷一些碎片视频啥的, 一直没有想过自己写一个插件
上面已经提到了, 这个插件已经有七个月了, 后来我也没有在关注这个插件, 把仓库也改为了 只读 状态, 但是昨天去公司加班(给其他部门产品), 发现在开发的产品用到的框架 vben v2 遇到相同的问题, 所以就想到了之前自己写的插件, 就改了一个地方适配了一下, 没想到还真的在这个产品也能用, 当时也是小激动了一把, 遂记录一下.
问题
七个月之前在用一个 vue-element-plus-admin 框架开发项目, 刚开始没有考虑项目部署后的一些问题, 后来发现项目是纯内网部署, 无法访问互联网, 发现部署后所有icon 都无法正常展示, 后来发现这个框架icon用的是iconify, 他会去线上请求icon到本地来渲染, 部署到内网后, 由于无法访问互联网, 所以icon就无法正常渲染
解决
当时在网上搜了解决方案, 基本方案如下
- 将icon下载放到本地
- 私有化部署 iconify api 服务, 然后在修改iconify 去请求的地址(已尝试, 确实可行)
- 其实是在文章写到这里时候我又去搜了下私有化部署, 结果发现了 unplugin-iconify这个库(心想: 我的天, 我跟个小丑一样当时还费劲巴拉的研究了半天vite插件机制, 怎么写插件, 我的天塌了😭, 不过既然文章写到了这里了, 还是坚持写完吧,😂)
搜完方案后, 想着私有化部署感觉还得在客户那边在部署一套 iconify api 服务(虽然是docker), 还得去让后端(或者售后)打包时候打进去, 就放弃了.
就想选择我把icon都下载下来放到本地, 突然又想到了 unocss 不是在打包时候去分析 class 然后动态生成css吗? 当时想: 他行我也行啊(有句话 初生牛犊不怕虎, 虽然年纪已不小(是老黄牛), 但是老黄牛也不能怕虎)
开发插件
说干就干, 立马打开 vite 官网, 找到 插件 部分, 开始看文档, 感觉 虚拟模块 就不错, unocss 用的也是虚拟模块
确定用虚拟模块后就开始干, 又遇到一个问题 unocss 在开发时, 就可以识别你的 class 去生成样式, 可以热更新, 但我当时的思路是 我默认你开发时是可以访问互联网的, 我就不做识别了, 只在 build 时候我去分析所有的 js jsx ts tsx vue文件, 找到所有的字符串变量, 看是不是匹配 icon 的写法, 如果符合我就保存, 整体分析完后 从 @iconify/json 里去拿到用到的icon后生成加载代码(模拟模块可以返回一串代码, 然后在项目里引用的时候会执行)
js
const code = `
import { addCollection } from '@iconify/iconify'
let iconList = ${JSON.stringify(
Object.values(iconJSON)
.map((item) => item.useJSON)
.filter((item) => item)
)}
iconList.forEach(item => addCollection(item))
export default iconList
`
return code
下面就是怎么去分析用什么去分析
看了 @vue/compiler-sfc 内部有用到 babel, 就直接用 babel 去解析js ts jsx tsx 文件,
vue文件由@vue/compiler-sfc解析后在交给 babel 解析, 这样就可以去分析生成的ASt, 拿到所有的字符串变量去匹配是不是符合 iconify 的icon格式, 先匹配开头 比如 el:
, 在去匹配之后的字符串, 解析完成后用 @iconify/json 和 @iconify/utils, 去拿到用到的icon的内容, 然后在用 @iconify/iconify 的 addCollection
方法去加载, 这样就完成了开发.
注意: 插件在开发阶段不会生效, 只会在 build
时候去运行, 所以开发阶段还是会去请求线上地址
使用
说了这么多, 在简单说下怎么使用吧(其实就是项目 README 里写的, 照搬过来)
install
npm install vite-plugin-iconify-vue-offline -D
配置
vite.config.ts
js
import type { UserConfig, ConfigEnv } from 'vite'
import Vue from '@vitejs/plugin-vue'
// 引入vite-plugin-iconify-vue-offline插件
import Icon from 'vite-plugin-iconify-vue-offline'
export default ({ command, mode }: ConfigEnv): UserConfig => {
let env = {} as any
const isBuild = command === 'build'
if (!isBuild) {
env = loadEnv(process.argv[3] === '--mode' ? process.argv[4] : process.argv[3], root)
} else {
env = loadEnv(mode, root)
}
return {
base: env.VITE_BASE_PATH,
plugins: [
// 在这里配置vite-plugin-iconify-vue-offline插件
Icon(),
Vue({
script: {
// 开启defineModel
defineModel: true
}
}),
VueJsx()
......
]
......
}
})
main.ts
在 main.ts 中引入 virtual:icon
ts
...
// 引入 virtual:icon
import 'virtual:icon'
...
仅需上面两个地方配置, 就可以完成
结尾
其实过程还是很坎坷的, 本来对什么 AST , 插件 都是只存在于听说过, 但是现在要去实际自己从0-1去开发, 还是有很多收获的
今天写的时候搜到 unplugin-iconify ,开始时候感觉好烦, 人家写的肯定比我好啊, 但是后来一想, 他写是他写, 我自己写也是一个学习啊(安慰自己 😂😂😂)
好了, 最后然后让我在放一个项目github地址吧, github.com/clddup/vite..., 走过路过的跪求个Star, 大佬路过别喷代码烂(第一次写, 哈哈)