Vue 3 模块化组件封装完全指南
组件设计原则单一职责可配置性可测试性松耦合性能优化合理使用 v-memo 和 v-once组件懒加载Props 变化监听优化代码质量保证TypeScript 类型检查单元测试覆盖ESLint 和 Prettier 规范文档维护组件示例Props 和事件文档更新日志模块化组件封装是一个持续优化的过程。通过遵循以上规范和最佳实践,我们可以构建出高质量、可维护、可复用的 Vue 3 组件库。记住:好的组
·
Vue 3 模块化组件封装完全指南
引言
在现代前端开发中,模块化组件是构建可维护和可扩展应用的关键。本文将详细介绍 Vue 3 中组件封装的最佳实践,从基础结构到高级特性,帮助你构建高质量的可复用组件。
基础组件结构
1. 目录结构规范
推荐的组件目录结构:
components/
└── MyComponent/
├── index.ts # 组件入口文件
├── src/
│ ├── types.ts # 类型定义
│ ├── hooks.ts # 组件相关的钩子函数
│ ├── constants.ts # 常量定义
│ └── utils.ts # 工具函数
├── styles/
│ └── index.scss # 样式文件
└── __tests__/ # 测试文件目录
└── index.test.ts
2. 组件基础模板
// types.ts
export interface Props {
title?: string
type?: 'primary' | 'secondary'
onClick?: (e: MouseEvent) => void
}
// index.ts
import { defineComponent } from 'vue'
import type { Props } from './src/types'
export default defineComponent({
name: 'MyComponent',
props: {
title: {
type: String,
default: ''
},
type: {
type: String as () => Props['type'],
default: 'primary'
}
},
emits: ['click'],
setup(props, { emit }) {
const handleClick = (e: MouseEvent) => {
emit('click', e)
}
return {
handleClick
}
}
})
组件封装技巧
1. 使用组合式函数(Composables)
// hooks.ts
import { ref, onMounted, onUnmounted } from 'vue'
export function useWindowSize() {
const width = ref(window.innerWidth)
const height = ref(window.innerHeight)
const update = () => {
width.value = window.innerWidth
height.value = window.innerHeight
}
onMounted(() => {
window.addEventListener('resize', update)
})
onUnmounted(() => {
window.removeEventListener('resize', update)
})
return {
width,
height
}
}
// 组件中使用
export default defineComponent({
setup() {
const { width, height } = useWindowSize()
return { width, height }
}
})
2. Props 和事件标准化
// types.ts
export interface TableColumn {
key: string
title: string
width?: number
sortable?: boolean
}
export interface TableProps {
columns: TableColumn[]
dataSource: Record<string, any>[]
loading?: boolean
}
export interface TableEmits {
(e: 'sort', key: string, order: 'asc' | 'desc'): void
(e: 'select', selectedRows: Record<string, any>[]): void
}
// Table.vue
<script setup lang="ts">
import type { TableProps, TableEmits } from './types'
const props = defineProps<TableProps>()
const emit = defineEmits<TableEmits>()
const handleSort = (key: string, order: 'asc' | 'desc') => {
emit('sort', key, order)
}
</script>
3. 插槽和作用域插槽
<template>
<div class="my-table">
<!-- 默认插槽 -->
<slot name="header">
<div class="table-header">
<h3>{{ title }}</h3>
<slot name="header-extra" />
</div>
</slot>
<!-- 作用域插槽 -->
<template v-for="item in dataSource" :key="item.id">
<slot name="row" :data="item" :index="index">
<div class="table-row">
{{ item.name }}
</div>
</slot>
</template>
<slot name="footer" />
</div>
</template>
4. 状态管理与依赖注入
// context.ts
import { provide, inject, InjectionKey } from 'vue'
export interface FormContext {
model: Record<string, any>
rules?: Record<string, any>
validate: () => Promise<boolean>
}
export const FormContextKey: InjectionKey<FormContext> = Symbol('FormContext')
export function provideForm(context: FormContext) {
provide(FormContextKey, context)
}
export function useForm() {
const form = inject(FormContextKey)
if (!form) throw new Error('useForm must be used within Form component')
return form
}
高级特性
1. 组件实例类型声明
// global.d.ts
declare module 'vue' {
export interface GlobalComponents {
'MyComponent': typeof import('./components/MyComponent')['default']
}
}
2. 可扩展的样式系统
// styles/variables.scss
$prefix: 'my-component';
$primary-color: var(--primary-color, #1890ff);
$font-size: var(--font-size, 14px);
// styles/mixins.scss
@mixin theme-light {
--primary-color: #1890ff;
--background-color: #ffffff;
}
@mixin theme-dark {
--primary-color: #177ddc;
--background-color: #141414;
}
// styles/index.scss
@import './variables.scss';
@import './mixins.scss';
.#{$prefix} {
font-size: $font-size;
&-header {
color: $primary-color;
}
&--dark {
@include theme-dark;
}
}
3. 组件文档生成
// MyComponent.vue
/**
* @component MyComponent
* @description 一个示例组件
* @example
* <MyComponent
* title="标题"
* type="primary"
* @click="handleClick"
* />
*/
export default defineComponent({
props: {
/**
* 组件标题
* @default ''
*/
title: String,
/**
* 组件类型
* @values primary, secondary
* @default 'primary'
*/
type: String
}
})
测试规范
// __tests__/index.test.ts
import { mount } from '@vue/test-utils'
import MyComponent from '../index'
describe('MyComponent', () => {
test('renders properly', () => {
const wrapper = mount(MyComponent, {
props: {
title: 'Test Title'
}
})
expect(wrapper.text()).toContain('Test Title')
})
test('emits click event', async () => {
const wrapper = mount(MyComponent)
await wrapper.trigger('click')
expect(wrapper.emitted('click')).toBeTruthy()
})
})
最佳实践总结
-
组件设计原则
- 单一职责
- 可配置性
- 可测试性
- 松耦合
-
性能优化
- 合理使用 v-memo 和 v-once
- 组件懒加载
- Props 变化监听优化
-
代码质量保证
- TypeScript 类型检查
- 单元测试覆盖
- ESLint 和 Prettier 规范
-
文档维护
- 组件示例
- Props 和事件文档
- 更新日志
结语
模块化组件封装是一个持续优化的过程。通过遵循以上规范和最佳实践,我们可以构建出高质量、可维护、可复用的 Vue 3 组件库。记住:好的组件设计应该是直观的、一致的、可测试的,并且有良好的文档支持。
参考资源
更多推荐


所有评论(0)