Vue3 自定义公共组件的实现实例
作者:Rysxt
本文主要介绍了Vue3 自定义公共组件的实现实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
一 环境准备与项目初始化
- 使用 Vite 快速搭建 Vue3 项目,选择 Vue 模板:
- 创建项目:
npm create vite@latest my-component-lib --template vue - 进入目录:
cd my-component-lib - 安装依赖:
npm install
- 创建项目:
推荐采用 monorepo 风格组织组件库源码,便于多组件维护与发布:
新建目录:packages/(组件源码)、src/(示例与文档)
示例结构:
my-component-lib/ ├── packages/ │ ├── button/ │ │ └── index.vue │ └── index.js ├── src/ ├── package.json └── vite.config.js
在 packages/index.js 统一导出组件,便于全局注册与按需引入。
二 编写一个可复用的按钮组件
- 组件功能:支持 size、type、disabled,通过 插槽 自定义内容,点击时 emit('click')。
- 组件代码:
packages/button/index.vue<template> <button :class="['zd-btn', sizeClass, typeClass, { 'is-disabled': disabled }]" :disabled="disabled" @click="handleClick" > <slot /> </button> </template> <script setup> import { computed } from 'vue' const props = defineProps({ size: { type: String, default: 'middle', validator: v => ['large', 'middle', 'small', 'mini'].includes(v) }, type: { type: String, default: 'default', validator: v => ['default', 'primary', 'success', 'warning', 'danger', 'info', 'text'].includes(v) }, disabled: { type: Boolean, default: false } }) const emit = defineEmits(['click']) const sizeClass = computed(() => `zd-btn--${props.size}`) const typeClass = computed(() => `zd-btn--${props.type}`) const handleClick = (e) => { if (!props.disabled) emit('click', e) } </script> <style scoped> .zd-btn { border: none; border-radius: 4px; padding: 0 16px; font-family: inherit; cursor: pointer; transition: all 0.2s ease; outline: none; } .zd-btn:disabled { cursor: not-allowed; opacity: 0.6; } .zd-btn--large { height: 48px; font-size: 16px; } .zd-btn--middle { height: 40px; font-size: 14px; } .zd-btn--small { height: 32px; font-size: 13px; } .zd-btn--mini { height: 24px; font-size: 12px; } .zd-btn--default { background: #fff; border: 1px solid #d9d9d9; color: #333; } .zd-btn--default:hover:not(.is-disabled) { border-color: #c0c0c0; background: #f5f5f5; } .zd-btn--primary { background: #409eff; color: #fff; border: 1px solid #409eff; } .zd-btn--primary:hover:not(.is-disabled) { background: #66b1ff; border-color: #66b1ff; } .zd-btn--success { background: #67c23a; color: #fff; border: 1px solid #67c23a; } .zd-btn--warning { background: #e6a23c; color: #fff; border: 1px solid #e6a23c; } .zd-btn--danger { background: #f56c6c; color: #fff; border: 1px solid #f56c6c; } .zd-btn--info { background: #909399; color: #fff; border: 1px solid #909399; } .zd-btn--text { background: transparent; border: none; color: #409eff; } .zd-btn--text:hover:not(.is-disabled) { background: rgba(64,158,255,0.1); } </style> - 入口导出:
packages/index.jsimport { App } from 'vue' import ZdButton from './button/index.vue' const components = [ZdButton] const install = (app) => { components.forEach(c => app.component(c.name || c.displayName, c)) } export { ZdButton, install } export default { install } - 本地测试:在
src/main.js全局注册并使用import { createApp } from 'vue' import App from './App.vue' import ZdComponentLib from '../packages/index.js' createApp(App).use(ZdComponentLib).mount('#app')<template> <zd-button type="primary" size="large" @click="onClick">Primary</zd-button> </template> <script setup> const onClick = () => alert('clicked') </script> - 要点
- 使用 defineProps / defineEmits 声明式 API,类型清晰。
- 通过 validator 约束枚举值,减少错误用法。
- 使用 scoped 样式避免泄漏,必要时用 CSS 变量 支持主题定制。
三 组件库打包与发布到 npm
- 库模式构建配置:
vite.config.jsimport { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import { resolve } from 'path' export default defineConfig({ plugins: [vue()], build: { lib: { entry: resolve(__dirname, 'packages/index.js'), name: 'MyUILib', fileName: (fmt) => `my-ui.${fmt}.js` }, rollupOptions: { external: ['vue'], output: { globals: { vue: 'Vue' } } } } }) - 入口包配置:
package.json(建议独立包名与版本管理){ "name": "@your-org/vue3-ui", "version": "1.0.0", "type": "module", "main": "dist/my-ui.umd.js", "module": "dist/my-ui.es.js", "files": ["dist", "packages"], "types": "dist/types/index.d.ts", "scripts": { "dev": "vite", "build": "vite build", "preview": "vite preview" }, "peerDependencies": { "vue": "^3.0.0" }, "keywords": ["vue3", "components", "ui"], "author": "Your Name", "license": "MIT" } - 类型声明(可选,提升 TS 体验):
dist/types/index.d.tsimport type { DefineComponent } from 'vue' export const ZdButton: DefineComponent<{ size?: 'large' | 'middle' | 'small' | 'mini' type?: 'default' | 'primary' | 'success' | 'warning' | 'danger' | 'info' | 'text' disabled?: boolean }, {}, {}, {}, { click: [MouseEvent] }> export const install: any export default { install } - 发布流程
- 登录 npm:
npm login(确保源为官方仓库) - 构建:
npm run build - 发布:
npm publish --access public
- 登录 npm:
- 其他项目使用
- 全局注册:
app.use(MyUILib) - 按需引入:
import { ZdButton } from '@your-org/vue3-ui'(并单独引入样式文件,如有)。
- 全局注册:
四 使用方式与最佳实践
- 两种引入方式
- 全局注册:适合通用 UI 组件,减少重复 import。
import { createApp } from 'vue' import App from './App.vue' import MyUILib from '@your-org/vue3-ui' app.use(MyUILib).mount('#app') - 按需引入:减小打包体积,配合构建工具自动引入(如 unplugin-vue-components)。
<template> <zd-button type="primary">Hello</zd-button> </template> <script setup> import { ZdButton } from '@your-org/vue3-ui' </script>
- 全局注册:适合通用 UI 组件,减少重复 import。
- 组件设计要点
- Props:明确 类型、默认值、校验器,保持 API 稳定。
- 事件:语义化命名(如 click、change),必要时透传事件对象与业务数据。
- 插槽:优先使用默认插槽与具名插槽提升可定制性。
- 样式:优先 scoped;跨组件主题用 CSS 变量;对外提供主题变量文件。
- 可访问性:支持键盘操作、焦点管理、ARIA 属性。
- 表单组件:优先支持 v-model,遵循原生表单元素交互约定。
- 简单测试示例(Jest + Vue Test Utils)
import { mount } from '@vue/test-utils' import { ZdButton } from '@your-org/vue3-ui' test('emits click when not disabled', async () => { const wrapper = mount(ZdButton) await wrapper.trigger('click') expect(wrapper.emitted('click')).toHaveLength(1) }) test('does not emit click when disabled', async () => { const wrapper = mount(ZdButton, { props: { disabled: true } }) await wrapper.trigger('click') expect(wrapper.emitted('click')).toBeUndefined() }) - 常见问题与排查
- 事件未触发:检查是否被 disabled 拦截或事件绑定写法是否正确。
- 样式冲突:确认使用 scoped 或命名规范(BEM)。
- 全局污染:避免在组件内使用过多样式全局选择器。
- 按需引入样式缺失:确认是否单独引入组件样式文件或配置了样式自动引入插件。
到此这篇关于Vue3 自定义公共组件的实现实例的文章就介绍到这了,更多相关Vue3 自定义公共组件内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
