Vue3 组件间复杂数据传递:父子、兄弟、跨层级通信全解析
在 Vue3 组件开发中,组件之间的数据传递是一个非常重要的功能,尤其是在复杂场景下,我们可能需要在父子、兄弟、跨层级组件之间进行数据共享或事件触发。
本篇文章将带你学习 Vue3 组件间的数据传递方式,并通过一个复杂的实战案例(父组件控制子组件、兄弟组件交互、跨层级全局数据通信)来掌握各种数据传递的技巧。
1. 组件间通信方式概述
通信方式 | 适用场景 | 具体方法 |
---|---|---|
Props/Emit | 父子组件 | props 向子组件传递数据,emit 触发事件通知父组件 |
$attrs & $listeners | 多级嵌套组件 | 透传 props 和 事件 到更深层组件 |
v-model | 父子双向绑定 | 用 v-model 绑定子组件的 value |
Mitt 事件总线 | 兄弟组件 | mitt 作为事件中转站 |
Pinia | 全局状态管理 | 跨组件、跨页面共享数据 |
Provide/Inject | 深层组件 | 祖先组件 provide 数据,子孙组件 inject |
2. 实战案例:构建一个 TodoList 应用,包含复杂组件通信
功能需求
- 父组件
App.vue
:管理任务列表,能够添加/删除任务。 - 子组件
TaskItem.vue
:显示任务信息,并支持切换完成状态。 - 兄弟组件
TaskFilter.vue
:控制任务筛选(全部、已完成、未完成)。 - 跨层级组件
TaskSummary.vue
:显示任务统计信息(使用Provide/Inject
)。 - 使用 Mitt 事件总线 处理兄弟组件的通信。
- 使用 Pinia 进行全局状态管理 让所有组件都能访问任务数据。
3. 搭建 Vue3 + Vite 项目
创建 Vue3 + Vite 项目:
npm create vite@latest vue-todo-complex --template vue
cd vue-todo-complex
npm install
npm install pinia mitt
4. 项目目录
vue-todo-complex
│── src
│ ├── components
│ │ ├── TaskItem.vue # 子组件(任务项)
│ │ ├── TaskFilter.vue # 兄弟组件(筛选任务)
│ │ ├── TaskSummary.vue # 跨层级组件(统计任务)
│ ├── stores
│ │ ├── taskStore.js # Pinia 任务状态管理
│ ├── App.vue # 父组件
│ ├── main.js # 入口文件
│── package.json
│── index.html
5. 任务状态管理(Pinia 全局状态)
在 stores/taskStore.js
里创建任务全局状态:
import { defineStore } from 'pinia';
export const useTaskStore = defineStore('taskStore', {
state: () => ({
tasks: [],
filter: 'all' // 可选值:all, completed, pending
}),
actions: {
addTask(text) {
this.tasks.push({ id: Date.now(), text, completed: false });
},
removeTask(id) {
this.tasks = this.tasks.filter(task => task.id !== id);
},
toggleTask(id) {
const task = this.tasks.find(task => task.id === id);
if (task) {
task.completed = !task.completed;
}
},
setFilter(filter) {
this.filter = filter;
}
},
getters: {
filteredTasks: (state) => {
if (state.filter === 'completed') {
return state.tasks.filter(task => task.completed);
} else if (state.filter === 'pending') {
return state.tasks.filter(task => !task.completed);
}
return state.tasks;
},
completedCount: (state) => state.tasks.filter(task => task.completed).length,
totalCount: (state) => state.tasks.length
}
});
6. 事件总线(mitt 处理兄弟组件通信)
在 src/mitt.js
创建事件总线:
import mitt from 'mitt';
export const eventBus = mitt();
7. 组件开发
7.1 TaskItem.vue
(子组件,接收 props
,触发 emit
事件)
<script setup>
import { defineProps, defineEmits } from 'vue';
const props = defineProps(['task']);
const emit = defineEmits(['remove', 'toggle']);
</script>
<template>
<li>
<input type="checkbox" :checked="task.completed" @change="emit('toggle', task.id)" />
<span :class="{ completed: task.completed }">{{ task.text }}</span>
<button @click="emit('remove', task.id)">删除</button>
</li>
</template>
<style scoped>
.completed {
text-decoration: line-through;
}
</style>
7.2 TaskFilter.vue
(兄弟组件,使用 mitt 事件总线)
<script setup>
import { eventBus } from '../mitt';
const setFilter = (filter) => {
eventBus.emit('filterChanged', filter);
};
</script>
<template>
<div>
<button @click="setFilter('all')">全部</button>
<button @click="setFilter('completed')">已完成</button>
<button @click="setFilter('pending')">未完成</button>
</div>
</template>
7.3 TaskSummary.vue
(跨层级组件,使用 Provide/Inject)
<script setup>
import { inject } from 'vue';
const completedCount = inject('completedCount');
const totalCount = inject('totalCount');
</script>
<template>
<div>
<p>已完成任务:{{ completedCount }} / {{ totalCount }}</p>
</div>
</template>
7.4 App.vue
(父组件,整合所有功能)
<script setup>
import { ref, provide, onMounted } from 'vue';
import { useTaskStore } from './stores/taskStore';
import { eventBus } from './mitt';
import TaskItem from './components/TaskItem.vue';
import TaskFilter from './components/TaskFilter.vue';
import TaskSummary from './components/TaskSummary.vue';
const taskStore = useTaskStore();
const newTask = ref('');
const addTask = () => {
if (newTask.value.trim()) {
taskStore.addTask(newTask.value);
newTask.value = '';
}
};
onMounted(() => {
eventBus.on('filterChanged', (filter) => {
taskStore.setFilter(filter);
});
});
// Provide 跨层级数据
provide('completedCount', taskStore.completedCount);
provide('totalCount', taskStore.totalCount);
</script>
<template>
<div>
<h1>Vue3 复杂组件通信</h1>
<input v-model="newTask" placeholder="输入任务..." />
<button @click="addTask">添加任务</button>
<TaskFilter />
<ul>
<TaskItem
v-for="task in taskStore.filteredTasks"
:key="task.id"
:task="task"
@remove="taskStore.removeTask"
@toggle="taskStore.toggleTask"
/>
</ul>
<TaskSummary />
</div>
</template>
8. 总结
✅ Props/Emit 实现父子通信
✅ Mitt 事件总线处理兄弟组件交互
✅ Pinia 全局状态管理
✅ Provide/Inject 实现跨层级通信
快来试试吧!🚀