Vue 3 与 Rollup 库开发
概述
Rollup 是一款高效的模块打包工具,特别适合构建 JavaScript 库和组件。与 Vite 不同,Rollup 专注于生成更小、更高效的代码,支持多种输出格式,是开发 Vue 3 组件库和工具库的理想选择。本集将深入探讨如何使用 Rollup 开发和构建 Vue 3 库,包括配置、打包策略、最佳实践和发布流程。
核心知识点
1. Rollup 基础配置
创建一个基础的 Rollup 配置文件 rollup.config.js:
// rollup.config.js
import vue from '@vitejs/plugin-vue'
import typescript from '@rollup/plugin-typescript'
import resolve from '@rollup/plugin-node-resolve'
import commonjs from '@rollup/plugin-commonjs'
import terser from '@rollup/plugin-terser'
export default {
input: 'src/index.ts', // 入口文件
output: [
// 多种输出格式
{
file: 'dist/library.esm.js',
format: 'es', // ES 模块
sourcemap: true
},
{
file: 'dist/library.cjs.js',
format: 'cjs', // CommonJS
sourcemap: true
},
{
file: 'dist/library.umd.js',
format: 'umd', // UMD 格式
name: 'MyLibrary', // 全局变量名
sourcemap: true,
globals: {
vue: 'Vue' // 外部依赖映射
}
}
],
plugins: [
vue(),
typescript(),
resolve(),
commonjs(),
terser() // 代码压缩
],
external: ['vue'] // 外部依赖,不打包到输出文件
}2. 项目结构设计
library-project/
├── src/
│ ├── components/ # Vue 组件
│ │ └── MyComponent.vue
│ ├── composables/ # 组合式函数
│ │ └── useMyFeature.ts
│ ├── utils/ # 工具函数
│ │ └── helpers.ts
│ └── index.ts # 入口文件,导出所有内容
├── rollup.config.js # Rollup 配置
├── tsconfig.json # TypeScript 配置
├── package.json # 项目配置
└── README.md # 项目说明3. 入口文件设计
入口文件 src/index.ts 用于导出库的所有公共 API:
// src/index.ts
// 导出组件
export { default as MyComponent } from './components/MyComponent.vue'
// 导出组合式函数
export { default as useMyFeature } from './composables/useMyFeature'
export * from './composables/useMyFeature'
// 导出工具函数
export * from './utils/helpers'4. 组件开发规范
开发 Vue 3 组件时,遵循以下规范:
<!-- src/components/MyComponent.vue -->
<template>
<div class="my-component">
<slot></slot>
</div>
</template>
<script setup lang="ts">
defineProps<{
// 定义组件属性
color?: string
size?: 'small' | 'medium' | 'large'
}>()
// 定义组件事件
const emit = defineEmits<{
(e: 'click', event: MouseEvent): void
}>()
</script>
<style scoped>
.my-component {
/* 组件样式 */
}
</style>5. 组合式函数设计
创建可复用的组合式函数:
// src/composables/useMyFeature.ts
import { ref, computed } from 'vue'
export default function useMyFeature(initialValue = 0) {
const count = ref(initialValue)
const doubled = computed(() => count.value * 2)
function increment() {
count.value++
}
return {
count,
doubled,
increment
}
}6. TypeScript 配置
配置 TypeScript 以支持 Vue 3 和 Rollup 构建:
// tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"skipLibCheck": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "preserve",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"declaration": true, // 生成类型声明文件
"emitDeclarationOnly": true,
"outDir": "dist",
"types": ["vite/client"]
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
"references": [{ "path": "./tsconfig.node.json" }]
}7. 打包命令配置
在 package.json 中配置打包命令:
{
"name": "my-vue3-library",
"version": "1.0.0",
"type": "module",
"main": "dist/library.cjs.js",
"module": "dist/library.esm.js",
"types": "dist/index.d.ts",
"exports": {
".": {
"import": "./dist/library.esm.js",
"require": "./dist/library.cjs.js"
}
},
"scripts": {
"build": "rollup -c",
"dev": "rollup -c -w"
},
"peerDependencies": {
"vue": "^3.3.0"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^25.0.0",
"@rollup/plugin-node-resolve": "^15.0.0",
"@rollup/plugin-terser": "^0.4.0",
"@rollup/plugin-typescript": "^11.0.0",
"@vitejs/plugin-vue": "^4.5.0",
"rollup": "^3.20.0",
"typescript": "^5.0.0",
"vue": "^3.3.0"
}
}8. 外部依赖处理
使用 external 选项处理外部依赖:
// rollup.config.js
export default {
// ...
external: (id) => {
// 外部依赖规则
return id.startsWith('vue') || id.startsWith('@vue/')
},
// ...
}9. 资源处理
使用插件处理静态资源:
// rollup.config.js
import url from '@rollup/plugin-url'
import image from '@rollup/plugin-image'
export default {
// ...
plugins: [
// ...
url({
limit: 8192, // 小于 8KB 的资源转为 base64
include: ['**/*.svg', '**/*.png', '**/*.jpg', '**/*.gif'],
emitFiles: true
}),
image()
],
// ...
}10. 多环境构建
配置不同环境的构建选项:
// rollup.config.js
import { defineConfig } from 'rollup'
const isProduction = process.env.NODE_ENV === 'production'
export default defineConfig({
// ...
output: [
// ...
{
// ...
plugins: [isProduction && terser()]
}
],
plugins: [
// ...
isProduction && terser()
].filter(Boolean),
// ...
})最佳实践
1. 类型安全
- 为所有导出的 API 添加完整的 TypeScript 类型定义
- 生成
.d.ts类型声明文件 - 使用
tsc --noEmit进行类型检查
2. 组件设计原则
- 单一职责原则:每个组件只负责一个功能
- 可配置性:通过 props 提供灵活的配置选项
- 可扩展性:支持插槽和自定义事件
- 无障碍支持:确保组件符合 accessibility 标准
3. 测试策略
- 单元测试:测试组件的各个功能点
- 集成测试:测试组件间的交互
- E2E 测试:测试组件在真实环境中的表现
- 快照测试:确保组件渲染结果一致
4. 文档编写
- 使用 Storybook 或 VitePress 生成组件文档
- 为每个 API 添加详细的 JSDoc 注释
- 提供使用示例和最佳实践
- 包含迁移指南和版本变更日志
5. 发布流程
- 确保所有测试通过
- 更新版本号(遵循 SemVer)
- 生成构建产物
- 提交代码并打标签
- 发布到 npm
- 更新文档
常见问题和解决方案
1. Vue 组件打包问题
问题:Vue 组件打包后无法正常使用
解决方案:
- 确保使用
@vitejs/plugin-vue处理 Vue 文件 - 检查 Vue 版本兼容性
- 确保组件使用正确的导出方式
2. 类型声明问题
问题:生成的类型声明文件不完整
解决方案:
- 配置
tsconfig.json中的declaration: true和emitDeclarationOnly: true - 使用
rollup-plugin-dts生成类型声明 - 手动检查和补充类型定义
3. 外部依赖冲突
问题:使用库时出现依赖冲突
解决方案:
- 在
peerDependencies中声明外部依赖 - 明确指定依赖版本范围
- 提供清晰的依赖安装指南
4. 资源路径问题
问题:打包后的资源路径不正确
解决方案:
- 使用
@rollup/plugin-url或@rollup/plugin-image处理资源 - 配置正确的
publicPath - 考虑使用相对路径或 CDN 资源
5. 多平台兼容性
问题:库在某些平台上无法正常工作
解决方案:
- 测试多种浏览器和 Node.js 版本
- 使用 Babel 进行代码转译
- 提供多种输出格式
- 避免使用平台特定的 API
进阶学习资源
官方文档:
优质插件:
学习案例:
- VueUse - 实用的 Vue 组合式函数库
- Naive UI - 现代化的 Vue 3 组件库
- Element Plus - 基于 Vue 3 的组件库
工具链:
- Changesets - 版本管理和发布工具
- Lerna - 多包管理工具
- Semantic Release - 自动发布工具
实践练习
练习 1:创建基础库
- 初始化一个 Rollup + Vue 3 库项目
- 实现一个简单的 Vue 组件
- 配置 Rollup 打包多种格式
- 生成类型声明文件
练习 2:组件库开发
- 创建一个包含多个组件的组件库
- 实现组合式函数和工具函数
- 配置资源处理和外部依赖
- 编写组件文档和示例
练习 3:高级配置
- 配置多环境构建(开发/生产)
- 实现代码分割和懒加载
- 配置资源优化和压缩
- 添加构建分析和可视化
练习 4:发布流程
- 配置 package.json 的各种字段
- 实现自动化测试和构建
- 创建 CHANGELOG 和版本管理
- 发布到 npm 并验证
总结
使用 Rollup 开发 Vue 3 库是一个高效、灵活的选择。通过合理的配置和最佳实践,你可以创建出高质量、易维护、广泛兼容的 Vue 3 库。从基础配置到高级优化,从组件设计到发布流程,掌握 Rollup 库开发技能将使你能够更好地贡献 Vue 3 生态系统。
下一集我们将学习 Vue 3 与 Vitest 高级测试,敬请期待!