Vue3企业级组件库架构设计:TypeScript+Vite从零搭建与自动化发布

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/

(0)
小编小编
上一篇 11小时前
下一篇 11小时前

相关推荐