{T}

组件库别名导出自动化:VP 组件前缀、类型正则提取与 custom resolver 铺垫

概述

这一节是 custom resolver 最关键的前置工程落地:给组件建立统一前缀、给类型建立统一别名,让自动化脚本不只会导出组件,还能导出“更像公共 API”的命名体系。难点在于类型导出要读取文件内容、先用正则剥离注释再匹配真实导出项,把内部类型模块整理成对外可消费的类型接口层。统一 VP 前缀本质上不是命名偏好,而是在为下一节自动导入建立稳定可识别模式。

学习目标

  • 理解统一前缀是 custom resolver 的前置工程,而非后置优化
  • 给所有组件统一前缀并建立长期公共命名体系
  • 处理历史 V 前缀组件去重,重建统一前缀
  • 组件前缀统一后,类型前缀也要同步,避免 API 语义割裂
  • 用脚本读取类型文件内容、先剥离注释再正则匹配真实导出项
  • 迭代正则覆盖泛型/别名等真实写法,公共类型先筛选再语义化

一、custom resolver 依赖稳定前缀模式

unplugin-vue-components 官方 README 明确支持 custom resolver,最典型写法就是“识别组件名前缀,再决定从哪个包导入”。如果库里 IconPickerHeaderVFormCollapseDescription 混用,自定义 resolver 很难优雅实现。先统一前缀,实际上是在为下一节自动导入做地基。

text
custom resolver 更喜欢:
  VPForm
  VPTable
  VPHeader

而不是:
  组件名风格完全混杂的一组导出

二、统一前缀建立长期公共命名体系

前面已处理 headermenu 这类保留字段冲突,这一节做更彻底的一步:给所有公共组件统一加前缀 VP。收益远大于“绕过规则报错”——宿主项目更容易区分组件来源、自定义 resolver 更容易写、类型命名和文档命名也更容易同步。前缀一旦定下,后续新增组件都应遵循。

js
const componentPrefix = 'VP'
let componentName = `${componentPrefix}${baseName}`

补充一点:eslint-plugin-vue 对保留组件名的敏感校验只是推动统一前缀的因素之一,更深层原因是组件库公共命名体系必须统一。

前缀化的价值不止于绕开规则报错,更在于让组件、类型、文档共享同一套命名语言。组件库和宿主一旦共用这套命名,接入成本会显著下降。

三、历史 V 前缀组件先去重再重建新前缀

有些组件原本就叫 VFormVTable,脚本无脑加前缀会变成 VPVFormVPVTable,让公共 API 失去一致性。脚本要加一层去重逻辑:若命中已带前缀的组件,先把旧前缀去掉,再统一拼新库前缀。这一步本质是在“清洗历史命名债务”,由自动化脚本统一应用规则最合适。

js
let componentName = baseName

if (needChangeName.includes(baseName)) {
  componentName = baseName.replace(/^V/, '')
}

componentName = `${componentPrefix}${componentName}`

四、组件前缀统一后,类型前缀也要同步

不是只改组件导出名,还要继续整理类型导出名。对使用者来说,最终接触到一整套 API:组件名、props 类型、schema 类型、事件类型。如果组件已是 VPFormVPTable,但类型仍叫 FormSchemaMenuSelectType,认知上就很割裂。公共类型应该跟公共组件名站在同一命名体系里。

text
组件名统一
  -> VPForm

类型名也应该统一
  -> VPFormSchema

五、类型导出要先读文件内容、用正则匹配真实导出项

组件导出较简单(文件名通常对应组件名),类型导出复杂很多:一个 types.ts 可能导出多个 interfacetypeenum,只看文件名没法知道该给哪些类型建别名。脚本要读取类型文件内容,用正则匹配导出项,再批量生成 VPXxx = module.Xxx 别名导出。这一步已从“简单路径扫描”进入“源码级别分析”,把脚本升级成一个小型 API 生成器。

text
读取文件内容
  -> 正则匹配 export interface / type / enum
  -> 提取导出名
  -> 生成带前缀的别名导出

六、正则匹配前先剥离注释,避免幽灵导出项

类型文件里有注释,注释里可能正好也包含 interface / type 关键字,结果会被正则误判成要导出的成员。所以真正匹配前,先把单行注释和多行注释从文本里剥离掉。只要还是用正则分析源码,这一步基本绕不开。

js
content = content.replace(/\/\/.*$/gm, '').replace(/\/\*[\s\S]*?\*\//g, '')

不先清注释,正则很容易匹配出“幽灵导出项”,让脚本工程接近真正的代码分析边界。

七、正则要覆盖泛型/别名等真实写法,公共类型先筛选再语义化

最开始的匹配过于简单,只能抓到最普通的导出,遇到泛型、复杂写法就漏掉。正则必须足够贴近项目真实类型书写习惯,所以要不断迭代匹配规则。同时,不是所有类型都应加前缀并公开:像 emitSelectTypeopenCloseType 这类内部命名,对外 API 显得生硬,要改造成 MenuSelectEventMenuOpenCloseEvent 这类更像公共 API 的名字。前缀化解决归属感,语义化解决可理解性。

text
内部命名
  emitSelectType

公共 API 命名
  MenuSelectEvent

常见问题

问题原因解决方案
后续想写 custom resolver,但组件名太乱组件名没有统一前缀模式先统一组件命名,再去写 resolver
给已有 VForm 再加前缀变成 VPVForm脚本没有处理历史前缀对已带 V 的组件先去重旧前缀
类型导出乱七八糟,甚至包含注释内容直接对原文件做正则匹配,没先清理注释先移除单行和多行注释,再做匹配
有些导出的 type/interface 漏掉了正则过于简单,没覆盖项目真实写法根据真实导出形式迭代正则规则
类型都导出了但名字很怪内部实现命名直接暴露成公共 API做语义化重命名,再建统一别名导出

延伸阅读