??Vue3 自定義公共組件的實(shí)現(xiàn)實(shí)例
???一 環(huán)境準(zhǔn)備與項(xiàng)目初始化??
- 使用 ??Vite?? 快速搭建 Vue3 項(xiàng)目,選擇 ??Vue?? 模板:
- 創(chuàng)建項(xiàng)目:
npm create vite@latest my-component-lib --template vue - 進(jìn)入目錄:
cd my-component-lib - 安裝依賴:
npm install
- 創(chuàng)建項(xiàng)目:
推薦采用 monorepo 風(fēng)格組織組件庫源碼,便于多組件維護(hù)與發(fā)布:
新建目錄:packages/(組件源碼)、src/(示例與文檔)
示例結(jié)構(gòu):
my-component-lib/ ├── packages/ │ ├── button/ │ │ └── index.vue │ └── index.js ├── src/ ├── package.json └── vite.config.js
在 packages/index.js 統(tǒng)一導(dǎo)出組件,便于全局注冊與按需引入。
??二 編寫一個(gè)可復(fù)用的按鈕組件??
- 組件功能:支持 ??size??、??type??、??disabled??,通過 ??插槽?? 自定義內(nèi)容,點(diǎn)擊時(shí) ??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> - 入口導(dǎo)出:
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> - 要點(diǎn)
- 使用 ??defineProps / defineEmits?? 聲明式 API,類型清晰。
- 通過 ??validator?? 約束枚舉值,減少錯(cuò)誤用法。
- 使用 ??scoped?? 樣式避免泄漏,必要時(shí)用 ??CSS 變量?? 支持主題定制。
??三 組件庫打包與發(fā)布到 npm??
- 庫模式構(gòu)建配置:
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(建議獨(dú)立包名與版本管理){ "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 體驗(yàn)):
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 } - 發(fā)布流程
- 登錄 npm:
npm login(確保源為官方倉庫) - 構(gòu)建:
npm run build - 發(fā)布:
npm publish --access public
- 登錄 npm:
- 其他項(xiàng)目使用
- 全局注冊:
app.use(MyUILib) - 按需引入:
import { ZdButton } from '@your-org/vue3-ui'(并單獨(dú)引入樣式文件,如有)。
- 全局注冊:
??四 使用方式與最佳實(shí)踐??
- 兩種引入方式
- 全局注冊:適合通用 UI 組件,減少重復(fù) import。
import { createApp } from 'vue' import App from './App.vue' import MyUILib from '@your-org/vue3-ui' app.use(MyUILib).mount('#app') - 按需引入:減小打包體積,配合構(gòu)建工具自動引入(如 unplugin-vue-components)。
<template> <zd-button type="primary">Hello</zd-button> </template> <script setup> import { ZdButton } from '@your-org/vue3-ui' </script>
- 全局注冊:適合通用 UI 組件,減少重復(fù) import。
- 組件設(shè)計(jì)要點(diǎn)
- Props:明確 ??類型??、??默認(rèn)值??、??校驗(yàn)器??,保持 API 穩(wěn)定。
- 事件:語義化命名(如 ??click??、??change??),必要時(shí)透傳事件對象與業(yè)務(wù)數(shù)據(jù)。
- 插槽:優(yōu)先使用默認(rèn)插槽與具名插槽提升可定制性。
- 樣式:優(yōu)先 ??scoped??;跨組件主題用 ??CSS 變量??;對外提供主題變量文件。
- 可訪問性:支持鍵盤操作、焦點(diǎn)管理、ARIA 屬性。
- 表單組件:優(yōu)先支持 ??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() }) - 常見問題與排查
- 事件未觸發(fā):檢查是否被 ??disabled?? 攔截或事件綁定寫法是否正確。
- 樣式?jīng)_突:確認(rèn)使用 ??scoped?? 或命名規(guī)范(BEM)。
- 全局污染:避免在組件內(nèi)使用過多樣式全局選擇器。
- 按需引入樣式缺失:確認(rèn)是否單獨(dú)引入組件樣式文件或配置了樣式自動引入插件。
到此這篇關(guān)于??Vue3 自定義公共組件的實(shí)現(xiàn)實(shí)例的文章就介紹到這了,更多相關(guān)??Vue3 自定義公共組件內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue/cli?配置動態(tài)代理無需重啟服務(wù)的操作方法
vue-cli是vue.js的腳手架,用于自動生成vue.js+webpack的項(xiàng)目模板,分為vue?init?webpack-simple?項(xiàng)目名和vue?init?webpack?項(xiàng)目名兩種,這篇文章主要介紹了vue/cli?配置動態(tài)代理,無需重啟服務(wù),需要的朋友可以參考下2022-05-05
vue 通過base64實(shí)現(xiàn)圖片下載功能
這篇文章主要介紹了vue 通過base64實(shí)現(xiàn)圖片下載功能,幫助大家更好的理解和使用vue框架,感興趣的朋友可以了解下2020-12-12
Vue el-table復(fù)選框全部勾選及勾選回顯功能實(shí)現(xiàn)
這篇文章主要介紹了Vue el-table復(fù)選框全部勾選及勾選回顯功能實(shí)現(xiàn),本文通過示例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2024-05-05
VUE項(xiàng)目中調(diào)用高德地圖的全流程講解
這篇文章主要介紹了VUE項(xiàng)目中調(diào)用高德地圖的全流程講解,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08

