Vue3 组件间复杂数据传递:父子、兄弟、跨层级通信全解析

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 应用,包含复杂组件通信

功能需求

  1. 父组件 App.vue:管理任务列表,能够添加/删除任务。
  2. 子组件 TaskItem.vue:显示任务信息,并支持切换完成状态。
  3. 兄弟组件 TaskFilter.vue:控制任务筛选(全部、已完成、未完成)。
  4. 跨层级组件 TaskSummary.vue:显示任务统计信息(使用 Provide/Inject)。
  5. 使用 Mitt 事件总线 处理兄弟组件的通信。
  6. 使用 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 实现跨层级通信

快来试试吧!🚀

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

全栈探索者chen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值