Vue3生态中,企业级组件库是前端工程化体系的核心基础设施。一套设计良好的组件库能统一团队UI规范、降低重复开发成本、提升页面一致性。本文从项目初始化、组件设计模式、按需加载、文档系统到CI/CD自动化发布,完整演示基于Vue3+TypeScript+Vite的组件库搭建流程。
组件库项目初始化与Monorepo工程配置
使用pnpm的workspace功能搭建Monorepo结构,将组件、工具函数、文档分包管理:
mkdir vue3-ui-lib && cd vue3-ui-lib
pnpm init
# 创建workspace配置
cat > pnpm-workspace.yaml << 'EOF'
packages:
- 'packages/*'
- 'docs'
- 'playground'
EOF
# 项目结构
# ├── packages/
# │ ├── components/ # 核心组件库
# │ ├── utils/ # 工具函数
# │ ├── theme-chalk/ # 样式主题
# │ └── icons/ # 图标组件
# ├── docs/ # VitePress文档
# ├── playground/ # 调试环境
# └── pnpm-workspace.yaml
# 安装核心依赖
pnpm add -D -w vite vue typescript @vitejs/plugin-vue
pnpm add -D -w @types/node sass unplugin-vue-components
pnpm add -w vue
组件库的Vite构建配置,支持ES模块和CommonJS双格式输出:
// packages/components/vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'
export default defineConfig({
plugins: [vue()],
build: {
target: 'es2018',
outDir: 'dist',
cssCodeSplit: true,
lib: {
entry: resolve(__dirname, 'src/index.ts'),
name: 'Vue3UI',
fileName: (format) => `vue3-ui.${format}.js`,
formats: ['es', 'cjs', 'umd']
},
rollupOptions: {
external: ['vue'],
output: {
globals: { vue: 'Vue' },
exports: 'named',
assetFileNames: (assetInfo) => {
if (assetInfo.name === 'style.css') return 'vue3-ui.css'
return assetInfo.name || '[name][extname]'
}
}
}
}
})
核心组件设计模式与TypeScript类型封装
Button组件作为基础组件,展示组件库的标准设计模式——Props定义、Slot设计、事件系统、样式隔离:
// packages/components/src/button/button.ts
import { defineComponent, computed } from 'vue'
import type { ExtractPropTypes, PropType } from 'vue'
export const buttonProps = {
type: {
type: String as PropType<'primary' | 'success' | 'warning' | 'danger' | 'default'>,
default: 'default'
},
size: {
type: String as PropType<'large' | 'default' | 'small'>,
default: 'default'
},
disabled: { type: Boolean, default: false },
loading: { type: Boolean, default: false },
round: { type: Boolean, default: false },
nativeType: {
type: String as PropType<'button' | 'submit' | 'reset'>,
default: 'button'
}
} as const
export type ButtonProps = ExtractPropTypes
export default defineComponent({
name: 'VuButton',
props: buttonProps,
emits: ['click'],
setup(props, { slots, emit }) {
const classes = computed(() => [
'vu-button',
`vu-button--${props.type}`,
`vu-button--${props.size}`,
{
'is-disabled': props.disabled,
'is-loading': props.loading,
'is-round': props.round
}
])
const handleClick = (e: MouseEvent) => {
if (props.disabled || props.loading) return
emit('click', e)
}
return () => (
<button
class={classes.value}
type={props.nativeType}
disabled={props.disabled || props.loading}
onClick={handleClick}
>
{props.loading && <span class="vu-button__loading" />}
{slots.default?.()}
</button>
)
}
})
Form表单组件展示组合式API和依赖注入模式:
// packages/components/src/form/form.ts
import { defineComponent, provide, reactive, toRefs } from 'vue'
import { formContextKey } from './context'
export const formProps = {
model: { type: Object, required: true },
rules: { type: Object, default: () => ({}) },
labelWidth: { type: String, default: '80px' },
labelPosition: {
type: String as PropType<'left' | 'right' | 'top'>,
default: 'right'
}
}
export default defineComponent({
name: 'VuForm',
props: formProps,
setup(props, { slots, expose }) {
const fields = reactive([])
const validate = async (callback?: Function) => {
const results = await Promise.all(
fields.map(field => field.validate())
)
const valid = results.every(Boolean)
callback?.(valid)
return valid
}
// 依赖注入:子组件FormItem通过inject获取
provide(formContextKey, {
...toRefs(props),
fields,
addField: (field) => fields.push(field),
removeField: (field) => {
const idx = fields.indexOf(field)
if (idx > -1) fields.splice(idx, 1)
}
})
expose({ validate })
return () => <form class="vu-form">{slots.default?.()}</form>
}
})
组件按需加载与Tree-shaking配置
按需加载依赖ES模块的静态分析和sideEffects配置。每个组件单独导出,打包工具自动摇除未使用的组件:
// packages/components/src/index.ts
export { default as Button } from './button'
export { default as Input } from './input'
export { default as Form, FormItem } from './form'
export { default as Select } from './select'
export { default as Table } from './table'
export { default as Modal } from './modal'
// 类型导出
export type { ButtonProps } from './button'
export type { FormProps } from './form'
// package.json
{
"name": "@vue3-ui/components",
"version": "1.0.0",
"main": "dist/vue3-ui.cjs.js",
"module": "dist/vue3-ui.es.js",
"types": "dist/index.d.ts",
"sideEffects": [
"*.css",
"*.scss"
],
"exports": {
".": {
"import": "./dist/vue3-ui.es.js",
"require": "./dist/vue3-ui.cjs.js",
"types": "./dist/index.d.ts"
},
"./es/button": "./dist/button/index.mjs",
"./es/input": "./dist/input/index.mjs",
"./style.css": "./dist/vue3-ui.css"
}
}
消费端配置unplugin-vue-components实现自动导入:
// 业务项目vite.config.ts
import Components from 'unplugin-vue-components/vite'
import { Vue3UIResolver } from './resolver'
export default defineConfig({
plugins: [
vue(),
Components({
resolvers: [
Vue3UIResolver({
importStyle: 'css' // 自动引入对应样式
})
],
dts: 'types/components.d.ts' // 生成类型声明
})
]
})
VitePress组件文档系统搭建与示例展示
// docs/.vitepress/config.ts
import { defineConfig } from 'vitepress'
export default defineConfig({
title: 'Vue3 UI',
description: '企业级Vue3组件库',
themeConfig: {
nav: [
{ text: '指南', link: '/guide/' },
{ text: '组件', link: '/components/button' }
],
sidebar: {
'/components/': [
{
text: '基础组件',
items: [
{ text: 'Button 按钮', link: '/components/button' },
{ text: 'Input 输入框', link: '/components/input' }
]
},
{
text: '表单组件',
items: [
{ text: 'Form 表单', link: '/components/form' },
{ text: 'Select 选择器', link: '/components/select' }
]
}
]
}
}
})
文档页面中通过Container组件实现代码示例与源码切换:
<!-- docs/components/button.md -->
# Button 按钮
常用的操作按钮组件。
## 基础用法
:::demo
<template>
<vu-button type="primary">主要按钮</vu-button>
<vu-button type="success">成功按钮</vu-button>
<vu-button type="danger">危险按钮</vu-button>
</template>
:::
## 禁用状态
:::demo
<template>
<vu-button disabled>禁用按钮</vu-button>
<vu-button loading>加载中</vu-button>
</template>
:::
CI/CD自动化发布与版本管理流程
使用GitHub Actions实现提交代码后自动构建、测试、发布npm包:
# .github/workflows/release.yml
name: Release
on:
push:
tags: ['v*']
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
with:
version: 9
- uses: actions/setup-node@v4
with:
node-version: 20
registry-url: https://registry.npmjs.org
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Type check
run: pnpm run type-check
- name: Unit tests
run: pnpm run test:unit
- name: Build
run: pnpm run build
- name: Generate changelog
run: npx changelogen --release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Publish to npm
run: pnpm publish --no-git-checks --recursive
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
组件库版本管理采用语义化版本(SemVer):MAJOR.MINOR.PATCH。破坏性API变更递增MAJOR,新增功能递增MINOR,Bug修复递增PATCH。使用changesets管理版本变更记录,开发者提交PR时附带changeset描述,发布时自动汇总生成CHANGELOG。这套流程确保每次发布都有完整的变更记录,消费端可精确评估升级影响。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/vue3-qi-ye-ji-zu-jian-ku-jia-gou-she-ji-typescriptvite-cong/