自动导入图标库和组件插件的改造实践

3 篇文章 0 订阅
2 篇文章 0 订阅

项目使用的打包工具为esbuild,目标为构建出一个图标库。然后图标库需要适配一个提供自动导入功能的vite开源插件arco-design/plugin-vite-vue,插件能让项目免于书写全局引入组件库或手动引入指定组件的代码,直接在Vue模板中书写对应的组件名或图标组件名即可自动导入使用。

[自动导入插件]  插件的总体思路比较简单:解析vue文件时,若监测有组件节点的名称以特定xxx开头,总体名称匹配上了库内的组件名以及其他节点信息匹配上了规则,则在webpack/vite解析vue文件和转化为JS文件时(通常在transform hook),在文件头部自动加上一句对应的导入语句即可。

关于自动导入插件

插件主要服务于一个UI组件库,该组件库的组件存放文件夹中还存放了一个icon文件夹(该文件夹内的图标转化为了.vue组件文件,后面将UI组件和图标组件统称为”组件“),插件默认以该组件库目录结构来进行代码解析。UI组件基本已满足业务效果,主要目标为适配一个自己项目的外部图标库。插件支持使用外部图标库,但解析时对图标库的路径结构,文件名称等配置有要求,因此需要改造该插件。

源码解析
  • 当配置了iconbox时,插件会尝试动态读取该名称的库的信息,使用了require进行导入读取,插件的输出产物为CommonJS模块。因此需要图标库导出一个CommonJS模块的产物才能进行读取。

  • 插件最初服务于arco-designUI组件库,该库内内置了一个图标库,因此插件默认也是以该库为目标模板结构开发的。插件的改造预期希望保留原来的效果不变(仍支持导入UI组件内的图标文件,虽然业务内基本不会使用),另外支持导入外部图标库依赖。因此需要区分内外图标库的格式,这里我直接用iconPrefix的名称来做逻辑区分了。

  • 插件为方便开发使用,效果设定为了开发环境(dev)时,首次监测到组件的使用时,自动导入的是1个内联所有组件bundle后的产物文件。打包时(build)则是按需引入的非内联的组件产物。

  • 关于模块的动态导入功能,是插件通过读取声明文件记录了所有组件的文件路径,要求文件为es模块且为非bundle模式的产物,因此还需要产出一个ES Module的包。(业务的UI组件库已经使用vite打包的lib库模式都有对应的产物。图标库目前仅有一个ESM包,需要改造)

关于图标库
图标组件的生成

图标为调用Figma的API,读取业务内UI设计师的图标库内容,生成svg格式文件到本地。通过Vue template的包裹和文件生成.vue文件,最终通过esbuild的api打包成.js文件产物。

关于esbuild

实践发现,esbuild适用于单文件入口,内联bundle打包的场景,如网页html等,而对于lib库模块esbuild并不适合,而vite显然更适合做这件事情。但是由于图标库最开始维护时就已经使用了esbuild,因此不打算重新修改为vite打包。

因此最终咱们的需求其实是解析每个目录下的组件文件(包含组件代码xxx.vue文件、组件入口文件index.ts、组件综合收集文件xxx-ui.ts和组件综合入口文件index.ts),并依次保持原目录结构,生成esm结构的js文件。但发现使用esbuild时,按照官网文档做了一些配置后,发现了一些问题:

  1. 打包产物配置为esm,将bundle改为false(必须)。如果入口文件是组件components文件夹外部的单文件index.ts,会发现无论怎么修改outdir、outbase等配置,都无法达到目的效果。即单文件入口状态下,如果不做内联,无法自动递归读取和解析文件。

  2. ts文件的导入另一个ts文件时,一般会要求去掉后缀。而esbuild对ts文件处理完后,会原原本本保留路径字符串的内容没有做更改。导致组件的入口index.js文件会出现诸如require('xxx/comp.vue')require('xxx/index')这种语句,但实际的产物都已经变成js文件了,导致了报错。

最终折腾半天,解决方案如下:

问题1:在打包单个组件的index.ts入口文件时,开启bundle模式,最终只生成一个.js文件,入口和组件合为一个。

问题2:选择预先读取和收集各类文件的路径形成数组文件,分别配置打包config,分批次打包。

关于图标库发布
  • 一个npm库,因为最开始默认都是CommonJS模块,因此package.json的main和type更加支持以CommonJS为产物的包。

  • type影响产物被引入时的模块化类型,咱们的插件需要用require导入图标库,那么就要求图标库为CommonJS模块,因此type字段必须为commonjs。但图标库整体又为es module格式书写的,甚至在模块的外部最顶层使用了await关键字,这在commonjs是不被允许的。(这里又折腾了半天)

  • 另外,es module支持导入commonjs模块,反之则不行。(谁叫人家esm后面出的呢......)

最终的解决方案暂时是开发时和打包修改type为module,发布时修改为commonjs。后续考虑写个发布前钩子脚本自动修改package.json文件的type。

其他一些遇到的问题

1、当vite图标库以单一esm包发布,插件为cjs无法导入esm产物,则想将插件改写为esm。实操并不大行。

  • 考虑插件依赖问题,插件内部依赖4、5个babel,打包时若内联引入进去,大大增加打包体积,不大行。将几个babel提升成生产时依赖,下载插件时能同步下载依赖包,运行时通过导入语法加载(esm下为import)。在启动运行时调试发现,esm的import语法并不能导入需要的包,会提示相关导入的变量不存在(导入失败了),而在运行时的.pnpm里是可以发现pnpm有下载babel到同级目录的。而require是支持直接导入的。

  • 当esm的插件依赖了一些cjs包,且内联进来时,打包工具会用转换语法把cjs的导入语法转换成esm模式的语法。但实际运行时还是会报错——esm不支持动态导入语法。

2、最终还是回到cjs插件+esm图标库

  • 发现插件导入图标库仅仅是为了获取所有图标名的map,于是最终方案为——直接在图标库提供一个json文件给出图标信息,插件仅请求这一个文件。而导入图标的语法实际为固定路径的拼接和esm导入语法字符串的生成(语法解析和代码转换),并没有真正去导入该图标。

3、esm模块导入文件时的问题,

  • esm模块的导入语法import xxx from 'aaa/bbb'。如果aaa和bbb都是目录,即使bbb下有一个index.js文件,实际在运行时是不支持的(插件运行环境下)。会提示esm不支持导入一个目录

  • 打包工具(esbuild,rollup)在打包ts项目时,并不支持能转换ts的路径解析结果。如import {a} from 'xxx/index',开发时,ts在一般路径解析规则(如node)中,可以不书写文件类型后缀,可以最后只精确到目录(会自动去搜索其下的index.js)。但打包后,打包工具并不会修改这个路径,最终产物里面只有个不带后缀的相对路径,esm并不支持。(可能需要先进行ts的解析,在进行打包文件)

写在最后

感觉,esm虽然为大势所趋,但只要node环境一直固定为commonjs存在且泛用,总会需要考虑这些个模块化兼容的问题。

”纸上写来终觉浅,深知此事要躬行“。

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值