Vue3组件通信的方法大全

 更新时间:2025年09月26日 08:59:20   作者:訾博ZiBo  
在Vue3中,组件通信仍然是一个非常重要的话题,因为在大多数应用程序中,不同的组件之间需要进行数据传递和交互,本文给大家介绍了Vue3组件通信的方法大全,需要的朋友可以参考下

一、 父子通信:最亲密、最常见的“家庭内部对话”

这是最基础也是最重要的通信方式,就像父母和孩子之间的日常交流。

1. Props (父 -> 子)

  • 一句话解释:父亲(父组件)递给儿子(子组件)一本《家规》(数据),儿子必须遵守。
  • 幽默比喻:这就是“龙生龙,凤生凤,老鼠的儿子会打洞”。父亲有什么“遗传基因”(数据),就通过props传给儿子。儿子可以“看”和“用”这本家规,但不能直接修改,否则就是“大逆不道”(单向数据流原则)。
  • 使用方法
  • 父组件 (Parent.vue):通过属性绑定的方式传值。
<template>
  <ChildComponent name="訾博" :age="18" />
</template>

<script setup>
import ChildComponent from './ChildComponent.vue';
</script>
  • 子组件 (ChildComponent.vue):使用defineProps接收。
<template>
  <p>大家好,我叫{{ name }},今年{{ age }}岁。</p>
</template>

<script setup>
// 接收父组件传来的props
const props = defineProps({
  name: String,
  age: {
    type: Number,
    required: true,
    default: 10
  }
});
</script>

2. Emits (子 -> 父)

  • 一句话解释:儿子(子组件)有事要向父亲(父组件)汇报,就大喊一声(触发事件)。
  • 幽默比喻:儿子在自己房间里考试得了100分,他不能直接冲到父亲的账本上把自己的零花钱改了。他得跑到客厅大喊:“爸!我考了100分!”($emit('got-full-marks', 100))。父亲听到了,自然会决定给他加多少零花钱(在父组件的事件监听函数里处理)。
  • 使用方法

子组件 (ChildComponent.vue):使用defineEmits声明事件,然后通过emit函数触发。

<template>
  <button @click="tellDad">告诉爸爸我长大了</button>
</template>

<script setup>
// 声明要触发的事件
const emit = defineEmits(['grow-up']);

function tellDad() {
  // 触发事件,并可以传递参数
  emit('grow-up', '我已经会自己写代码了!');
}
</script>

父组件 (Parent.vue):在子组件标签上使用@v-on监听事件。

<template>
  <ChildComponent @grow-up="handleChildGrowUp" />
  <p>来自儿子的消息:{{ messageFromChild }}</p>
</template>

<script setup>
import { ref } from 'vue';
import ChildComponent from './ChildComponent.vue';

const messageFromChild = ref('');

function handleChildGrowUp(message) {
  messageFromChild.value = message;
}
</script>

3. v-model (双向绑定语法糖)

  • 一句话解释propsemits的组合套餐,专门用于父子组件间的数据同步。
  • 幽默比喻:这就像一个对讲机。父亲可以通过对讲机向儿子发号施令(props),儿子也能通过同一个对讲机回应情况(emits),两边信息实时同步。
  • 使用方法:Vue3的v-model更灵活,可以有多个。

子组件 (CustomInput.vue)

<template>
  <input :value="modelValue" @input="emit('update:modelValue', $event.target.value)" />
</template>

<script setup>
defineProps(['modelValue']);
const emit = defineEmits(['update:modelValue']);
</script>

父组件 (Parent.vue)

<template>
  <CustomInput v-model="searchText" />
  <p>你在搜索:{{ searchText }}</p>
</template>

<script setup>
import { ref } from 'vue';
import CustomInput from './CustomInput.vue';

const searchText = ref('');
</script>

4. ref / $refs (父 -> 子)

  • 一句话解释:父亲直接拿到儿子的“遥控器”,可以命令儿子做某些具体动作(调用方法、访问属性)。
  • 幽默比喻:这有点“霸道总裁”,不通过商量(事件),直接命令。比如父亲想让儿子现在立刻马上去做个俯卧撑。这种方式不常用,因为它破坏了组件的封装性,属于“最后的手段”。
  • 使用方法

子组件 (ChildComponent.vue):使用defineExpose暴露方法或属性。

<template>
  <p>我是一个深藏不露的组件</p>
</template>

<script setup>
import { ref } from 'vue';

const doPushUp = () => {
  console.log('正在做俯卧撑...');
};

// 把doPushUp方法暴露给父组件
defineExpose({
  doPushUp
});
</script>

父组件 (Parent.vue)

<template>
  <ChildComponent ref="childRef" />
  <button @click="commandChild">命令儿子</button>
</template>

<script setup>
import { ref } from 'vue';
import ChildComponent from './ChildComponent.vue';

const childRef = ref(null);

function commandChild() {
  // 通过.value访问到子组件实例,并调用其暴露的方法
  childRef.value.doPushUp();
}
</script>

二、 跨代通信:当“爷爷”想和“孙子”说话

如果组件嵌套很深,一层一层地props下去,会累死人,这叫“Props Drilling”(属性钻探地狱)。为了避免这种情况,我们有更优雅的方案。

5. Provide / Inject

  • 一句话解释:祖先组件提供一个“共享宝藏”,任何后代组件都可以按“暗号”注入并使用它。
  • 幽默比喻:这就像你们訾家的祖传秘方。老祖宗(祖先组件)通过provide把秘方藏在一个地方,并告诉后代“暗号”是什么。任何一个姓訾的子孙(后代组件),无论隔了多少代,只要报出暗号(inject),就能拿到这个秘方。外人(非后代组件)是拿不到的。
  • 使用方法

祖先组件 (GrandParent.vue)

<template>
  <ParentComponent />
</template>

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

const themeColor = ref('dark');
// 提供数据,'theme'是暗号
provide('theme', themeColor); 
</script>

后代组件 (GrandChild.vue)

<template>
  <div :style="{ color: theme }">我是孙子组件,我用的是祖传主题色。</div>
</template>

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

// 注入数据,'theme'是暗号
const theme = inject('theme');
</script>

三、 任意组件通信:当“隔壁老王”想找你聊天

对于两个没有任何亲缘关系的组件,如何进行交流?

6. Pinia (官方推荐的状态管理库)

  • 一句话解释:建立一个“全局中央银行”(Store),所有组件都可以来这里存钱(修改state)、取钱(读取state)或办理业务(调用actions)。
  • 幽默比喻:想象一下,整个应用是一个城市,Pinia就是这个城市的中央银行。无论你是住在城东的A组件,还是城西的B组件,大家共用一个银行账户。A组件往里存了100块,B组件马上就能查到余额变化。它规范、安全、可追溯(有DevTools),是管理复杂应用状态的“金标准”。
  • 使用方法

定义Store (/stores/user.js)

import { defineStore } from 'pinia';

export const useUserStore = defineStore('user', {
  state: () => ({
    name: '訾博',
    isLoggedIn: false,
  }),
  actions: {
    login() {
      this.isLoggedIn = true;
    },
  },
});

在任意组件中使用

<template>
  <p v-if="userStore.isLoggedIn">欢迎回来, {{ userStore.name }}!</p>
  <button @click="userStore.login()">登录</button>
</template>

<script setup>
import { useUserStore } from '@/stores/user';

const userStore = useUserStore();
</script>

7. Mitt / Tiny-emitter (全局事件总线)

  • 一句话解释:创建一个全局的“广播站”,一个组件可以向某个频道广播,另一个组件可以收听这个频道。
  • 幽默比喻:Vue2时代的EventBus就像一个公共的对讲机频道。任何人都可以拿起来喊话,也任何人都可以收听。这在小型应用里很方便,但在大型应用里,你不知道是谁在喊话,也不知道谁在听,容易造成混乱。Vue3官方移除了这个功能,但我们可以用mitt这样的小库来自己实现。
  • 使用方法

创建广播站 (/utils/emitter.js)

import mitt from 'mitt';
const emitter = mitt();
export default emitter;

组件A (发送方)

<script setup>
import emitter from '@/utils/emitter';

function sendMessage() {
  emitter.emit('some-event', '来自A组件的问候');
}
</script>

组件B (接收方)

<script setup>
import emitter from '@/utils/emitter';
import { onMounted, onUnmounted } from 'vue';

onMounted(() => {
  emitter.on('some-event', (message) => {
    console.log(message); // "来自A组件的问候"
  });
});

// 记住,一定要在组件卸载时取消监听,否则会内存泄漏!
onUnmounted(() => {
  emitter.off('some-event');
});
</script>

总结与老师的忠告

訾博同学,我们来画个重点,做个总结:

场景推荐方法核心思想推荐指数
父 -> 子Props单向数据流,清晰明了★★★★★
子 -> 父Emits事件触发,解耦★★★★★
父子双向绑定v-modelprops和emits的语法糖★★★★★
跨代通信Provide / Inject依赖注入,避免属性钻探★★★★☆
复杂应用/任意组件Pinia集中式状态管理,可预测★★★★★
简单应用/任意组件Mitt (事件总线)发布订阅,简单灵活★★★☆☆
父组件调用子组件方法ref / defineExpose直接引用,应急手段★★☆☆☆

为师的忠告

  1. 首选“家庭内部”方案:优先使用 PropsEmits。这是Vue设计的核心,能让你的数据流向最清晰、最容易维护。
  2. 避免“属性钻探”:当 Props 需要传递超过两层时,就应该立刻考虑使用 Provide / Inject
  3. 拥抱“中央银行”:当多个组件共享同一份状态(比如用户信息、主题设置),并且这个状态会被多个组件修改时,不要犹豫,直接上 Pinia。它能让你的应用状态变得井井有条,而不是一团乱麻。
  4. 慎用“大喇叭”和“遥控器”Mittref 都有其用武之地,但它们也更容易导致代码难以追踪和维护。把它们当作你的“秘密武器”,非必要不使用。

专用于背诵的内容

訾博,把下面这段口诀背下来,面试和实战中定能助你一臂之力!

Vue3通信口诀

父传子,用 Props,儿子不能随便改。
子传父,靠 Emits,爹听事件乐开怀。
双向绑,v-model,省事方便真不赖。
爷孙传,ProvideInject 注入接过来。
兄弟情,状态乱,Pinia 银行管起来。
事件总线 Mitt 在,偶尔用用别依赖。
父控子,用 refexpose 暴露才存在。
牢记最佳实践,代码整洁人人爱!

以上就是Vue3组件通信的方法大全的详细内容,更多关于Vue3组件通信的资料请关注脚本之家其它相关文章!

相关文章

  • Vue实现数据筛选与搜索功能的示例代码

    Vue实现数据筛选与搜索功能的示例代码

    在许多Web应用程序中,数据筛选和搜索是关键的用户体验功能,本文将深入探讨在Vue中如何进行数据筛选与搜索的实现,感兴趣的小伙伴可以跟随小编一起学习一下
    2023-10-10
  • 基于element-ui封装表单金额输入框的方法示例

    基于element-ui封装表单金额输入框的方法示例

    这篇文章主要介绍了基于element-ui封装表单金额输入框的方法示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-01-01
  • vue项目配置使用flow类型检查的步骤

    vue项目配置使用flow类型检查的步骤

    这篇文章主要介绍了vue项目配置使用flow类型检查的步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-03-03
  • 详细解读VUE父子组件的使用

    详细解读VUE父子组件的使用

    这篇文章主要介绍了详细解读VUE父子组件的使用,今天来讲一种子父组件深度耦合的方式,使我们不用额外的创建新的组件,也可以达到一些效果,下面与你们分享一下
    2023-05-05
  • vscode+vue cli3.0创建项目配置Prettier+eslint方式

    vscode+vue cli3.0创建项目配置Prettier+eslint方式

    这篇文章主要介绍了vscode+vue cli3.0创建项目配置Prettier+eslint方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-10-10
  • Vue非单文件组件使用详解

    Vue非单文件组件使用详解

    Vue单文件组件通常使用.vue文件扩展名,下面这篇文章主要给大家介绍了关于Vue非单文件组件使用的相关资料,文中通过图文以及实例代码介绍的非常详细,需要的朋友可以参考下
    2023-06-06
  • vue路由监听的一些常用方式

    vue路由监听的一些常用方式

    有时在页面刷新或者返回等操作时,想监听路由变化进行数据更新等操作,下面这篇文章主要给大家介绍了关于vue路由监听的一些常用方式,需要的朋友可以参考下
    2023-10-10
  • vue-cli+iview项目打包上线之后图标不显示问题及解决方法

    vue-cli+iview项目打包上线之后图标不显示问题及解决方法

    这篇文章主要介绍了解决vue-cli+iview项目打包上线之后图标不显示问题,本文通过两种方法给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-10-10
  • 解决element-ui el-drawer抽屉el-dialog弹框关闭优化demo

    解决element-ui el-drawer抽屉el-dialog弹框关闭优化demo

    这篇文章主要为大家介绍了解决element-ui el-drawer抽屉el-dialog弹框关闭优化demo,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪<BR>
    2023-06-06
  • 深入探讨Vue 3中的组合式函数编程方式

    深入探讨Vue 3中的组合式函数编程方式

    Vue 3中引入了组合式函数编程方式,可以更好地实现代码的复用和可维护性。通过定义可组合的函数,可以将组件的逻辑和状态进行拆分和组合,实现更灵活的代码组织方式。同时,组合式函数也支持响应式数据和生命周期钩子函数,更加贴近Vue开发的实际场景
    2023-05-05

最新评论