Vue3 + Vite 组件库开发工具链:从构建到发布的自动化流水线
Vue3 Vite 组件库开发工具链从构建到发布的自动化流水线一、组件库开发的工程陷阱为什么你的可复用组件只在你自己项目中能用构建可复用的 UI 组件库听起来很简单写好组件npm publish搞定。但实际操作时你会撞上一系列工程问题TypeScript 类型声明怎么导出CSS 样式怎么处理Tree Shaking 怎么保证文档怎么和代码同步更新版本号怎么管理ChangeLog 怎么生成单元测试怎么跑E2E 测试怎么自动化截图对比这些问题单独拎出来都不难但串在一起会消耗大量的工程时间。你需要一套完整的工具链来自动化处理这些重复劳动。graph LR A[开发组件] -- B[Vite 开发服务器br/Storybook 预览] B -- C[编写单元测试br/Vitest] C -- D[编写 Storybr/自动生成文档] D -- E[提交代码] E -- F{CI 检查} F --|通过| G[自动构建] F --|失败| H[修复后重新提交] G -- I[自动发版br/ChangeLog 生成] I -- J[npm publish] subgraph 本地开发 A B C D end subgraph CI/CD E F G I J end style J fill:#51cf66,color:#fff style H fill:#ff6b6b,color:#fff本文将基于 Vue3 Vite 构建一套完整的组件库开发工具链覆盖构建配置、文档生成、测试策略和自动发版四个环节。二、Vite Library Mode 的底层机制ESM/CJS 双输出的打包策略Vite 的 Library Mode 与传统应用模式有本质区别。应用模式下Vite 使用 Rollup 打包并注入运行时 polyfill如 import.meta、动态导入。Library Mode 则更接近纯粹的 Rollup 打包产物需要同时兼容 ESM 和 CJS。配置的核心是build.lib// vite.config.ts import { defineConfig } from vite; import vue from vitejs/plugin-vue; import dts from vite-plugin-dts; export default defineConfig({ plugins: [ vue(), dts({ insertTypesEntry: true, include: [src/**/*.ts, src/**/*.vue], outDir: dist/types, }), ], build: { lib: { entry: src/index.ts, name: MyUI, formats: [es, cjs], fileName: (format) index.${format es ? mjs : cjs}, }, rollupOptions: { external: [vue], output: { globals: { vue: Vue }, preserveModules: true, preserveModulesRoot: src, }, }, }, });preserveModules: true是关键——它让 Rollup 保留模块结构而非全部打包到一个文件。消费者可以通过import { Button } from my-ui/Button按需加载而不是被迫加载整个my-ui。vite-plugin-dts处理 TypeScript 类型声明生成。insertTypesEntry: true会在package.json中自动添加types字段指向生成的类型入口文件。另一个关键配置是package.json的exports字段{ name: my-ui, type: module, main: ./dist/index.cjs, module: ./dist/index.mjs, types: ./dist/types/index.d.ts, exports: { .: { import: ./dist/index.mjs, require: ./dist/index.cjs, types: ./dist/types/index.d.ts }, ./*: ./dist/* }, sideEffects: false, files: [dist] }sideEffects: false告诉 bundler如 webpack可以安全地 Tree Shaking 这个包——因为纯 UI 组件库确实没有副作用。三、完整的组件库工具链实现首先是组件开发规范。每个组件需要包含组件实现.vue、单元测试.spec.ts、Story.stories.ts、类型导出index.ts。// src/Button/index.ts export { default as Button } from ./Button.vue; export type { ButtonProps, ButtonSize, ButtonVariant } from ./types;!-- src/Button/Button.vue -- script setup langts import type { ButtonProps } from ./types; const props withDefaults(definePropsButtonProps(), { size: medium, variant: primary, disabled: false, }); const emit defineEmits{ click: [e: MouseEvent]; }(); const handleClick (e: MouseEvent) { if (props.disabled) return; emit(click, e); }; /script template button :class[btn, btn--${props.size}, btn--${props.variant}] :disabledprops.disabled clickhandleClick slot / /button /template style scoped .btn { border: none; border-radius: 6px; cursor: pointer; font-weight: 500; transition: all 0.2s; } .btn--primary { background: #4dabf7; color: #fff; } .btn--secondary { background: #e9ecef; color: #333; } .btn--small { padding: 4px 12px; font-size: 12px; } .btn--medium { padding: 8px 20px; font-size: 14px; } .btn--large { padding: 12px 28px; font-size: 16px; } .btn:disabled { opacity: 0.5; cursor: not-allowed; } /style测试配置vitest.config.tsimport { defineConfig } from vitest/config; import vue from vitejs/plugin-vue; export default defineConfig({ plugins: [vue()], test: { environment: jsdom, coverage: { provider: v8, reporter: [text, lcov], include: [src/**/*.{ts,vue}], exclude: [src/**/*.stories.ts, src/**/index.ts], }, }, });自动发版的 GitHub Actions 配置name: Release on: push: branches: [main] jobs: release: runs-on: ubuntu-latest permissions: contents: write issues: write pull-requests: write steps: - uses: actions/checkoutv4 - uses: pnpm/action-setupv2 - uses: actions/setup-nodev4 with: node-version: 20 cache: pnpm - run: pnpm install --frozen-lockfile - run: pnpm test - run: pnpm build - name: Release env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: npx semantic-releasesemantic-release自动分析 commit message需要遵循 Conventional Commits 规范生成 ChangeLog计算下一个版本号创建 GitHub Release 并发布到 npm。四、工具链的维护成本与选型边界复杂度成本引入 Storybook semantic-release vitest vite-plugin-dts 会带来额外的依赖和维护成本。对于个人项目或 5 个组件以内的小型组件库只需要 vitest 手动发布。工具链的复杂度应该和库的规模成正比。CI 时间E2E 测试特别是截图对比测试会显著增加 CI 运行时间。如果组件库的样式变更频率很低将 E2E 测试设为手动触发而非每次 PR 都跑可以节省大量 CI 分钟数。版本管理陷阱semantic-release 依赖规范的 commit message。如果团队不遵守 Conventional Commitssemantic-release 只会一直发布 patch 版本。建议搭配 commitlint 做提交时的消息检查。不适用场景只有 1-2 个组件的项目工具链成本 组件开发成本团队不愿遵循 Conventional Commitssemantic-release 无法发挥价值需求频繁变化的业务组件库组件 API 不稳定版本管理收益有限五、总结Vue3 Vite 组件库开发工具链的核心组件Vite Library Mode构建、vite-plugin-dts类型生成、Storybook文档与预览、Vitest单元测试、semantic-release自动发版。落地路径从最简单开始——手动 npm publish然后逐步添加 vitest → vite-plugin-dts → Storybook → semantic-release。每一步单独验证确认收益后再加下一步。不要上来就把所有工具都配齐那是工程化的反面。少即是多。工具链的价值在于减少重复劳动而非增加配置复杂度。