11Vue3 组件系统详解
一、组件基本概念
1.1 什么是组件
组件是Vue应用的可复用实例,包含:
-
模板 (Template) - HTML结构
-
数据 (Data) - 响应式状态
-
方法 (Methods) - 交互逻辑
-
生命周期钩子 (Lifecycle Hooks)
1.2 组件注册
全局注册
import { createApp } from 'vue'
const app = createApp({})
// 全局注册组件
app.component('MyComponent', {
template: '<div>全局组件</div>'
})
局部注册(推荐)
import MyComponent from './MyComponent.vue' export default { components: { MyComponent } }
二、单文件组件 (SFC)
2.1 基本结构
<template>
<!-- HTML模板 -->
<div>{{ message }}</div>
</template>
<script setup>
// 逻辑部分
import { ref } from 'vue'
const message = ref('Hello Vue 3!')
</script>
<style scoped>
/* 样式部分 */
div {
color: red;
}
</style>
2.2 <script setup> 语法糖(Composition API)
<script setup> import { ref, computed, onMounted } from 'vue' // 响应式数据 const count = ref(0) const double = computed(() => count.value * 2) // 方法 function increment() { count.value++ } // 生命周期 onMounted(() => { console.log('组件已挂载') }) // 自动暴露给模板 defineExpose({ count, increment }) </script>
三、Props 组件通信
3.1 定义Props
<!-- 子组件 --> <script setup> // 方式1:TypeScript类型 const props = defineProps<{ title: string count?: number items: string[] }>() // 方式2:运行时声明 const props = defineProps({ title: { type: String, required: true, default: '默认标题' }, count: { type: Number, validator: (value) => value >= 0 } }) </script> <template> <h2>{{ title }}</h2> <p>数量: {{ count }}</p> </template>
3.2 使用Props
<!-- 父组件 --> <template> <ChildComponent :title="pageTitle" :count="itemCount" :items="listItems" /> </template>
四、自定义事件
4.1 定义和触发事件
<!-- 子组件 --> <script setup> const emit = defineEmits<{ // 带参数的事件 (e: 'update:count', value: number): void // 不带参数的事件 (e: 'submit'): void }>() function handleClick() { emit('update:count', 10) emit('submit') } </script>
4.2 使用v-model(双向绑定)
<!-- 自定义输入组件 --> <script setup> const modelValue = defineModel() function updateValue(newValue) { modelValue.value = newValue } </script>
五、插槽 (Slots)
5.1 默认插槽
<!-- 子组件 -->
<template>
<div class="card">
<slot>默认内容</slot>
</div>
</template>
<!-- 父组件使用 -->
<Card>
<p>自定义内容</p>
</Card>
5.2 具名插槽
<!-- 子组件 -->
<template>
<div class="layout">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
</template>
<!-- 父组件使用 -->
<Layout>
<template #header>
<h1>标题</h1>
</template>
<p>主要内容</p>
<template #footer>
<p>页脚</p>
</template>
</Layout>
5.3 作用域插槽
<!-- 子组件 -->
<template>
<ul>
<li v-for="item in items" :key="item.id">
<slot :item="item" :index="index"></slot>
</li>
</ul>
</template>
<!-- 父组件使用 -->
<ItemList :items="items">
<template v-slot="{ item, index }">
<span>{{ index + 1 }}. {{ item.name }}</span>
</template>
</ItemList>
六、组件生命周期
<script setup> import { onBeforeMount, // 挂载前 onMounted, // 挂载后 onBeforeUpdate, // 更新前 onUpdated, // 更新后 onBeforeUnmount, // 卸载前 onUnmounted, // 卸载后 onErrorCaptured // 错误捕获 } from 'vue' onMounted(() => { console.log('组件已挂载') // DOM操作、API请求 }) onUnmounted(() => { console.log('组件已卸载') // 清理定时器、取消订阅 }) </script>
七、依赖注入 (Provide/Inject)
7.1 提供数据
<!-- 祖先组件 --> <script setup> import { provide, ref } from 'vue' const theme = ref('dark') const user = ref({ name: '张三' }) // 提供响应式数据 provide('theme', theme) provide('user', user) // 提供方法 provide('updateTheme', (newTheme) => { theme.value = newTheme }) </script>
7.2 注入数据
<!-- 后代组件 --> <script setup> import { inject } from 'vue' // 注入数据 const theme = inject('theme') const user = inject('user') const updateTheme = inject('updateTheme') // 带默认值 const config = inject('config', { color: 'blue' }) // 工厂函数 const api = inject('api', () => defaultApi()) </script>
八、动态组件
<template> <component :is="currentComponent" v-bind="componentProps" /> </template> <script setup> import { shallowRef } from 'vue' import ComponentA from './ComponentA.vue' import ComponentB from './ComponentB.vue' const currentComponent = shallowRef(ComponentA) const componentProps = { title: '动态组件' } // 切换组件 function switchComponent(component) { currentComponent.value = component } </script>
九、异步组件
<script setup> import { defineAsyncComponent } from 'vue' // 基础用法 const AsyncComponent = defineAsyncComponent(() => import('./MyComponent.vue') ) // 带配置选项 const AsyncComponentWithOptions = defineAsyncComponent({ loader: () => import('./MyComponent.vue'), loadingComponent: LoadingComponent, errorComponent: ErrorComponent, delay: 200, timeout: 3000, suspensible: false, onError(error, retry, fail, attempts) { if (error.message.includes('fetch') && attempts <= 3) { retry() } else { fail() } } }) </script>
十、组件最佳实践
10.1 命名规范
// 文件名: PascalCase // MyComponent.vue // 组件名: PascalCase(模板中使用) app.component('MyComponent', { /* ... */ }) // Props: camelCase(定义),kebab-case(使用) defineProps({ userName: String // 定义时用camelCase }) // 使用时: <Component user-name="John" />
10.2 组件设计原则
-
单一职责:每个组件只做一件事
-
可复用性:通过props和slots提高复用性
-
组合优于继承:使用组合API构建复杂功能
-
响应式数据:合理使用ref和reactive
-
性能优化:使用v-memo、shallowRef等
10.3 组件通信选择
父 → 子:Props 子 → 父:自定义事件 兄弟组件:共同父级或Event Bus 深层嵌套:Provide/Inject 复杂状态:Pinia/Vuex
十一、示例:完整的TodoItem组件
<!-- TodoItem.vue --> <template> <li class="todo-item" :class="{ completed: todo.completed }" > <input type="checkbox" :checked="todo.completed" @change="toggleComplete" /> <span class="todo-text"> <slot :todo="todo"> {{ todo.text }} </slot> </span> <button @click="$emit('delete', todo.id)"> 删除 </button> </li> </template> <script setup> import { computed } from 'vue' const props = defineProps({ todo: { type: Object, required: true } }) const emit = defineEmits(['update:completed', 'delete']) const todoStatus = computed({ get: () => props.todo.completed, set: (value) => emit('update:completed', value) }) function toggleComplete() { todoStatus.value = !todoStatus.value } </script> <style scoped> .todo-item { display: flex; align-items: center; padding: 8px; border-bottom: 1px solid #eee; } .todo-item.completed .todo-text { text-decoration: line-through; color: #999; } </style>
总结
Vue 3的组件系统提供了:
-
两种API风格:Options API(兼容Vue 2)和Composition API(推荐)
-
更好的TypeScript支持:完整的类型推断
-
更灵活的组合:Composition API的逻辑复用
-
更好的性能:更小的包体积、更快的渲染
-
更清晰的代码组织:
<script setup>语法糖
掌握这些组件概念和用法,能够帮助你构建更健壮、可维护的Vue 3应用。

浙公网安备 33010602011771号