vue3封装公用组件

vue3   2025-03-27 11:40   32   0  

一、基础组件封装

1. 按钮组件示例

vue<!-- components/BaseButton.vue --><template>
  <button 
    :type="type" 
    :class="['base-btn', sizeClass]" 
    @click="$emit('click', $event)"
  >
    <slot>默认按钮</slot>
  </button>
</template>

<script setup>
defineProps({
  type: {
    type: String,
    default: 'button',
    validator: (v) => ['button', 'submit', 'reset'].includes(v)
  },
  size: {
    type: String,
    default: 'medium',
    validator: (v) => ['small', 'medium', 'large'].includes(v)
  }
})

defineEmits(['click'])

const sizeClass = computed(() => `btn-${props.size}`)
</script>

<style scoped>
.base-btn {
  padding: 8px 16px;
  border-radius: 4px;
  cursor: pointer;
}
.btn-small { font-size: 12px; }
.btn-medium { font-size: 14px; }
.btn-large { font-size: 16px; }
</style>

二、高级组件模式

1. 带插槽的 Modal 组件

vue<!-- components/BaseModal.vue --><template>
  <div v-show="visible" class="modal-mask">
    <div class="modal-wrapper">
      <div class="modal-container">
        <div class="modal-header">
          <slot name="header">
            <h3>{{ title }}</h3>
          </slot>
          <button @click="close">×</button>
        </div>

        <div class="modal-body">
          <slot></slot>
        </div>

        <div class="modal-footer">
          <slot name="footer">
            <BaseButton @click="close">关闭</BaseButton>
          </slot>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
defineProps({
  visible: Boolean,
  title: String
})

const emit = defineEmits(['update:visible'])

const close = () => {
  emit('update:visible', false)
}
</script>

2. 使用组件

vue<template>  <BaseModal v-model:visible="showModal" title="提示">
    <p>这是模态框内容</p>
    <template #footer>
      <BaseButton @click="confirm">确认</BaseButton>
    </template>
  </BaseModal>
</template>

三、组件通信模式

1. 复合组件(类似 Select + Option)

vue<!-- components/MySelect.vue --><template>
  <div class="select-wrapper">
    <div class="selected" @click="toggle">
      {{ selectedLabel }}
    </div>
    <div v-show="isOpen" class="options">
      <slot></slot>
    </div>
  </div>
</template>

<script setup>
import { provide, ref } from 'vue'

const isOpen = ref(false)
const selectedLabel = ref('请选择')
const selectedValue = ref(null)

const toggle = () => (isOpen.value = !isOpen.value)

const selectOption = (value, label) => {
  selectedValue.value = value
  selectedLabel.value = label
  isOpen.value = false
}

// 向子组件提供上下文
provide('selectContext', { selectOption })
</script>
vue<!-- components/MyOption.vue --><template>
  <div class="option" @click="handleClick">
    <slot></slot>
  </div>
</template>

<script setup>
import { inject } from 'vue'

const props = defineProps({
  value: [String, Number],
  label: String
})

const { selectOption } = inject('selectContext')

const handleClick = () => {
  selectOption(props.value, props.label || props.value)
}
</script>

四、最佳实践

1. 组件设计原则

  • 单一职责原则:每个组件只专注一个功能

  • 可配置性:通过 Props 控制组件行为

  • 可扩展性:使用 Slots 允许内容定制

  • 无障碍访问:添加 ARIA 属性

2. 性能优化技巧

vue<!-- 懒加载组件 --><script setup>
import { defineAsyncComponent } from 'vue'

const HeavyComponent = defineAsyncComponent(() =>
  import('./HeavyComponent.vue')
)
</script>

五、组件文档规范

1. 使用 TypeScript 类型定义

typescript// types/components.d.tsdeclare module '*.vue' {  import type { DefineComponent } from 'vue'  const component: DefineComponent<{}, {}, any>  export default component}// 按钮组件 Propsexport interface ButtonProps {  type?: 'primary' | 'success' | 'warning'  size?: 'small' | 'medium' | 'large'}

2. 编写组件文档

markdown## BaseButton 按钮### Props| 属性 | 类型 | 默认值 | 说明 ||------|------|--------|-----|| type | String | 'primary' | 按钮类型 || size | String | 'medium' | 按钮尺寸 |### Slots| 名称 | 说明 ||------|------|| default | 按钮内容 |### 示例```vue<BaseButton type="success" @click="handleClick">  提交</BaseButton>

六、组件全局注册

1. 全局注册方案

typescript// main.tsimport { createApp } from 'vue'import App from './App.vue'// 自动全局注册const app = createApp(App)const modules = import.meta.glob('./components/base/*.vue')Object.entries(modules).forEach(([path, module]) => {  const name = path.split('/').pop()?.replace('.vue', '')  app.component(name!, defineAsyncComponent(module))})app.mount('#app')

七、组件测试

1. Vitest 单元测试示例

typescript// tests/BaseButton.spec.tsimport { mount } from '@vue/test-utils'import BaseButton from '../components/BaseButton.vue'test('emits click event', async () => {  const wrapper = mount(BaseButton)  await wrapper.trigger('click')  expect(wrapper.emitted().click).toBeTruthy()})test('displays slot content', () => {  const wrapper = mount(BaseButton, {    slots: { default: '测试按钮' }  })  expect(wrapper.text()).toContain('测试按钮')})

八、组件对比(Vue 2 vs Vue 3)

特性Vue 2Vue 3
组件定义Options APIComposition API + <script setup>
Props 类型校验prop: { type: Object }TypeScript 接口 + PropType
事件通信this.$emitdefineEmits
插槽语法slot 和 slot-scopev-slot 和 # 简写
全局组件注册Vue.componentapp.component

总结建议

  1. 保持组件原子化:每个组件只解决一个具体问题

  2. 合理使用 Composition API:复杂逻辑使用 hooks 抽离

  3. 类型安全:结合 TypeScript 强化组件接口

  4. 文档驱动:为每个组件编写使用文档

  5. 自动化测试:保证组件质量的关键手段

通过以上模式,您可以创建出高复用性、易维护的 Vue 3 公共组件库。


博客评论
还没有人评论,赶紧抢个沙发~
发表评论
说明:请文明发言,共建和谐网络,您的个人信息不会被公开显示。