自动化脚本优化:完成组件类型导出
概述
在组件库项目中,除了 Components 和 Composables 之外,还需要导出 Directives(指令)模块和工具函数,使整个组件库具备完整的外部引用能力。本节介绍如何将指令模块从 admin 项目迁移到组件库,集成到全局导出入口,并完成类型声明文件的生成。
模块导出架构
组件库导出结构
├── components/ ← 组件(已处理)
├── composables/ ← 组合式函数(已处理)
├── directives/ ← 指令(本节处理)
│ └── index.ts ← 指令入口文件
│ └── setupDirectives(app: App): void
└── utils/ ← 工具函数(可选导出)
text
指令模块导出实现
1. 复制指令目录到组件库项目
将 admin 项目中的 directives/ 目录复制到组件库的 src/ 下,目录结构如下:
src/directives/
├── index.ts # 入口文件,导出 setupDirectives
├── copy.ts # 复制指令
├── debounce.ts # 防抖指令
├── throttle.ts # 节流指令
└── ...
text
2. 修改指令入口文件
将指令入口文件改为导出 setupDirectives 函数。注意不能直接 export 后就在入口文件中使用,需要先 import 再调用:
// src/directives/index.ts
import type { App } from 'vue'
import vCopy from './copy'
import vDebounce from './debounce'
import vThrottle from './throttle'
const directives = [vCopy, vDebounce, vThrottle]
// 全局注册函数,供 app.use() 时调用
export function setupDirectives(app: App) {
directives.forEach((directive) => {
if (directive.name && directive.directive) {
app.directive(directive.name, directive.directive)
}
})
}
// 同时导出各个指令,支持按需导入
export { vCopy, vDebounce, vThrottle }
ts
每个指令文件的结构约定:
// src/directives/copy.ts
import type { Directive } from 'vue'
export const vCopy: {
name: string
directive: Directive
} = {
name: 'copy',
directive(el, binding) {
el.addEventListener('click', () => {
navigator.clipboard.writeText(binding.value)
})
},
}
export default vCopy
ts
3. 在组件库主入口注册指令
// src/main.ts
import type { App, Plugin } from 'vue'
import { setupComponents } from './components'
import { setupDirectives } from './directives'
import * as components from './components'
const install: Plugin = {
install(app: App) {
// 组件全局注册
setupComponents(app)
// 指令全局注册
setupDirectives(app)
},
}
export default install
export * from './components'
export * from './directives'
export * from './composables'
ts
4. 导出方式的重构细节
从 export directives as directives module 改造为普通写法:
// 改造前(re-export 语法,不能直接在当前文件调用)
export { setupDirectives } from './directives'
// 改造后(先 import,再 export)
import { setupDirectives } from './directives'
// 在 install 函数中调用
setupDirectives(app)
// 底部统一 re-export
export * from './directives'
ts
Vue 3 Plugin 系统与指令注册
Vue 3 的插件系统通过 app.use(plugin) 安装,插件对象需要实现 install(app, options) 方法。指令注册使用 app.directive(name, definition):
// Vue 3 Plugin 接口
interface Plugin {
install(app: App, ...options: any[]): void
}
// 全局注册指令
app.directive('copy', {
mounted(el, binding) {
// 指令逻辑
},
})
ts
关键点:app.use() 会自动调用 install 方法,并且同一个插件只会安装一次(重复调用会被忽略)。
类型声明文件生成
自动化脚本扩展
在构建脚本中为指令生成类型声明:
// scripts/build-types.ts
import { readdirSync, writeFileSync } from 'fs'
import { resolve, basename } from 'path'
function capitalize(str: string): string {
return str.charAt(0).toUpperCase() + str.slice(1)
}
function generateDirectiveTypes() {
const directivesDir = resolve('src/directives')
const files = readdirSync(directivesDir).filter(
(f) => f.endsWith('.ts') && f !== 'index.ts'
)
const exports = files.map((file) => {
const name = basename(file, '.ts')
return `export const v${capitalize(name)}: Directive`
})
const content = `import type { Directive } from 'vue'\n\n${exports.join('\n')}`
writeFileSync(resolve('dist/types/directives.d.ts'), content)
}
ts
构建工具配合
类型导出需要与构建流程配合,确保 .d.ts 文件正确生成:
| 构建工具 | 类型生成配置 | 说明 |
|---|---|---|
| unbuild | build.types: true | 自动从 TS 源码生成 .d.ts |
| tsup | dts: true | 基于 tsc 生成声明文件 |
| Vite Library Mode | 需配合 vite-plugin-dts | Vite 本身不直接生成类型 |
Utils 模块的处理策略
| 方案 | 适用场景 | 优缺点 |
|---|---|---|
| 随组件库导出 | 工具函数通用性高、与组件耦合度低 | 增加包体积,但使用便捷 |
| 独立发包 | 工具函数复杂度高、可独立使用 | 增加维护成本,但职责清晰 |
| 不导出 | 工具函数仅服务于内部组件 | 零额外成本,但外部无法使用 |
本项目选择不单独导出 utils 模块,因为其中的方法都是针对自身组件编写的基础工具函数(如 table 相关工具、表单处理等),不具备通用性。如果未来 utils 足够复杂且具备独立使用价值,可以封装为共享库单独发包。
完整的组件库导出清单
| 模块 | 导出内容 | 导出方式 |
|---|---|---|
| Components | 所有 UI 组件 | app.use() 全局注册 + 按需导入 |
| Composables | useXxx() 组合式函数 | 命名导出 |
| Directives | v-copy、v-debounce 等 | app.use() 全局注册 |
| Types | 组件 Props、Emits 类型 | 类型声明文件 |
实践要点
- 指令全局注册与组件全局注册采用相同的
app.use()模式,保持 API 一致性 setupDirectives命名遵循 Element Plus 的setupDirectives约定- re-export 语法(
export ... from)导出的成员不能在当前文件直接使用,需要先import - 类型导出需要与构建流程(unbuild/tsup)配合,确保
.d.ts文件正确生成 - 对于大型组件库,建议将 directives 拆分为独立子包
↑