{T}

组件库 Vite 接入模块打包:vite-plugin 产物导出、NodeNext 配置与宿主最终闭环

概述

这一节的重点不是再发明新的接入协议,而是把上一节生成的 vite-plugin.ts 真正变成可发布、可安装、可被宿主导入的产物。组件库内部已具备运行时入口、组件自动注册插件、hooks / types / directives 导出,以及 VPAutoImportsVPComponentsResolver,但这些“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'。这是把“源码约定”升级成“发布约定”,辅助模块和主入口一样需要明确对外暴露。

text
源码存在
  !=
包对外可用

还需要:
  1. 构建产物
  2. exports 子路径

二、给 vite-plugin.ts 单独走 tsup 打包链

选择很务实的方案:用 tsup 单独打包 src/vite-plugin.ts,输出 ESM / CJS / DTS。这比把它硬塞进主组件构建链更直接,vite-plugin.ts 是宿主 vite.config.ts 要消费的构建期模块,不是运行时 UI 代码,给它单独一条微型打包链路更易控、更直观。运行时代码和构建期辅助模块本来就不该一视同仁。

bash
npx tsup src/vite-plugin.ts --format esm,cjs --dts

三、exports 增加 ./vite-plugin 子路径入口

package.json 增加 ./vite-plugin 导出子路径,和前面 ./style.css./locales/* 是同一类问题:文件在 dist 中存在,不代表包对外就能用它,只有进入 exports 才是正式公共 API。宿主后面就能稳定按子路径消费,不需要再猜目录结构。

json
{
  "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 / moduleResolutionNodeNext 很合理。这不是说整个项目必须切到 NodeNext,而是对“特定入口模块”单独使用一份更贴合 Node 侧消费的 tsconfig,多 tsconfig 拆分在组件库工程里非常常见。

json
{
  "compilerOptions": {
    "module": "NodeNext",
    "moduleResolution": "NodeNext"
  }
}

五、导出配置让接入协议版本随包走

VPAutoImportsVPComponentsResolver 只是写在宿主 vite.config.ts 里时,容易漂移:包名改了、hooks 列表变了、指令解析规则变了,宿主却没同步改。当它们被组件库自己导出后,接入规则就和组件库版本绑定,宿主安装哪个版本就拿哪个版本的接入协议。这一步真正降低的是“长期维护成本”,已把组件库从“代码集合”推向“接入产品”。

ts
import { VPAutoImports, VPComponentsResolver } from 'el-admin-components/vite-plugin'

六、三者一起使用才真正闭环

最终要达成组件自动导入、hooks 自动导入、directives 正常生效,三者分工明确:VPComponentsResolver 负责组件自动导入,VPAutoImports 负责 hooks 自动导入,setupDirectives(app) 负责把指令真正挂到应用上。缺一个宿主体验都不完整。指令目前仍更适合走整体注册,不是每类能力都必须走自动导入。

text
组件      -> resolver
hooks     -> auto imports
directives -> setupDirectives(app)

七、验证标准是能删掉手写 import

最后不是只看页面回来没,而是不断去删顶部手写组件 import、手写 hooks import,再观察系统能否继续正常运行。自动导入链路验证的真正标准是:不是你多配了几个插件,而是你真的删掉那些原本手写的导入后,页面还能跑。这一步比单纯看 vite.config.ts 更有说服力。

text
真正验证标准:
  删掉手写 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 包含它们

延伸阅读