组件库 Vite 接入模块打包:vite-plugin 产物导出、NodeNext 配置与宿主最终闭环
概述
这一节的重点不是再发明新的接入协议,而是把上一节生成的 vite-plugin.ts 真正变成可发布、可安装、可被宿主导入的产物。组件库内部已具备运行时入口、组件自动注册插件、hooks / types / directives 导出,以及 VPAutoImports 和 VPComponentsResolver,但这些“Vite 接入辅助能力”若只留在源码目录,还不算真正对外可用。要给 vite-plugin.ts 单独做构建输出、在 package.json.exports 声明子路径入口,并让宿主最终把组件、hooks、directives 的自动接入链路全部打通。
学习目标
- 理解
vite-plugin.ts要单独构建进 dist 并通过 exports 暴露 - 用
tsup单独打包vite-plugin.ts(ESM / CJS / DTS) - 在
package.json.exports增加./vite-plugin子路径入口 - 理解
NodeNext是针对该构建期模块的单独 TS 配置 - 理解导出配置让接入协议版本随包走
- 三者(resolver / auto-imports / setupDirectives)打通才是真正闭环
- 验证标准是删掉手写 import 页面仍能跑
一、vite-plugin.ts 要构建进 dist 并通过 exports 暴露
前面脚本已生成 src/vite-plugin.ts,但那还只在源码层。对宿主真正可用的前提是:它在 dist 里有产物、在 package.json.exports 里有明确入口,否则宿主虽“理论上知道有这个文件”,却无法稳定 import { VPAutoImports } from 'el-admin-components/vite-plugin'。这是把“源码约定”升级成“发布约定”,辅助模块和主入口一样需要明确对外暴露。
源码存在
!=
包对外可用
还需要:
1. 构建产物
2. exports 子路径二、给 vite-plugin.ts 单独走 tsup 打包链
选择很务实的方案:用 tsup 单独打包 src/vite-plugin.ts,输出 ESM / CJS / DTS。这比把它硬塞进主组件构建链更直接,vite-plugin.ts 是宿主 vite.config.ts 要消费的构建期模块,不是运行时 UI 代码,给它单独一条微型打包链路更易控、更直观。运行时代码和构建期辅助模块本来就不该一视同仁。
npx tsup src/vite-plugin.ts --format esm,cjs --dts三、exports 增加 ./vite-plugin 子路径入口
给 package.json 增加 ./vite-plugin 导出子路径,和前面 ./style.css、./locales/* 是同一类问题:文件在 dist 中存在,不代表包对外就能用它,只有进入 exports 才是正式公共 API。宿主后面就能稳定按子路径消费,不需要再猜目录结构。
{
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/el-admin-components.js",
"require": "./dist/el-admin-components.umd.cjs"
},
"./vite-plugin": {
"types": "./dist/vite-plugin.d.ts",
"import": "./dist/vite-plugin.js",
"require": "./dist/vite-plugin.cjs"
}
}
}四、NodeNext 是针对构建期模块的单独配置
打包 vite-plugin.ts 时遇到 TS 解析不到某些模块,提示需要 moduleResolution: "NodeNext"。因为这个文件是给构建工具链消费的,导入方式和当前 Node 的模块解析语义更贴近,单独搞一份 tsconfig.node.json 并设置 module / moduleResolution 为 NodeNext 很合理。这不是说整个项目必须切到 NodeNext,而是对“特定入口模块”单独使用一份更贴合 Node 侧消费的 tsconfig,多 tsconfig 拆分在组件库工程里非常常见。
{
"compilerOptions": {
"module": "NodeNext",
"moduleResolution": "NodeNext"
}
}五、导出配置让接入协议版本随包走
当 VPAutoImports 和 VPComponentsResolver 只是写在宿主 vite.config.ts 里时,容易漂移:包名改了、hooks 列表变了、指令解析规则变了,宿主却没同步改。当它们被组件库自己导出后,接入规则就和组件库版本绑定,宿主安装哪个版本就拿哪个版本的接入协议。这一步真正降低的是“长期维护成本”,已把组件库从“代码集合”推向“接入产品”。
import { VPAutoImports, VPComponentsResolver } from 'el-admin-components/vite-plugin'六、三者一起使用才真正闭环
最终要达成组件自动导入、hooks 自动导入、directives 正常生效,三者分工明确:VPComponentsResolver 负责组件自动导入,VPAutoImports 负责 hooks 自动导入,setupDirectives(app) 负责把指令真正挂到应用上。缺一个宿主体验都不完整。指令目前仍更适合走整体注册,不是每类能力都必须走自动导入。
组件 -> resolver
hooks -> auto imports
directives -> setupDirectives(app)七、验证标准是能删掉手写 import
最后不是只看页面回来没,而是不断去删顶部手写组件 import、手写 hooks import,再观察系统能否继续正常运行。自动导入链路验证的真正标准是:不是你多配了几个插件,而是你真的删掉那些原本手写的导入后,页面还能跑。这一步比单纯看 vite.config.ts 更有说服力。
真正验证标准:
删掉手写 import
页面仍然正常常见问题
| 问题 | 原因 | 解决方案 |
|---|---|---|
| vite-plugin.ts 在源码里但宿主导入失败 | 没单独打包产物,也没在 exports 暴露子路径 | 给它单独建构建脚本,并配置 ./vite-plugin 导出 |
| tsup 打包提示模块解析问题 | 当前 TS 配置和 Node/ESM 解析语义不匹配 | 单独用 tsconfig.node.json,设 module / moduleResolution 为 NodeNext |
| 组件自动导入好了但 hooks 还要手写 import | 只接了 Components(),没接 VPAutoImports | 在宿主 AutoImport() 中消费组件库导出的配置片段 |
| 页面还是缺指令效果 | setupDirectives(app) 没有调用 | 在宿主入口显式补链 |
| 配置都写了但 IDE 仍报错 | components.d.ts / auto-imports.d.ts 没刷新或 tsconfig 未感知 | 重新生成声明文件,并确保 tsconfig 包含它们 |