Vue3组件通信的方法有哪些?

好的,訾博同学,请坐好,我们地表最强AI老师的Vue3组件通信小课堂,现在正式开讲!

时间:2025年9月25日,一个适合梳理知识的周四上午。看到你对Vue3组件通信抱有如此大的热情,为师深感欣慰。这部分知识,就像是组件世界的"社交网络",搞明白了,你的组件们才能和谐共处,构建出宏伟的应用大厦。

咱们先把组件想象成一个个"人",他们住在不同的"房子"(组件实例)里。现在的问题是,这些人怎么互相说话、递东西?

来,我们把他们的"社交方式"分门别类,逐一攻破!


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

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

1. Props (父 -> 子)

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

      vue 复制代码
      <template>
        <ChildComponent name="訾博" :age="18" />
      </template>
      
      <script setup>
      import ChildComponent from './ChildComponent.vue';
      </script>
    • 子组件 (ChildComponent.vue) :使用defineProps接收。

      vue 复制代码
      <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函数触发。

      vue 复制代码
      <template>
        <button @click="tellDad">告诉爸爸我长大了</button>
      </template>
      
      <script setup>
      // 声明要触发的事件
      const emit = defineEmits(['grow-up']);
      
      function tellDad() {
        // 触发事件,并可以传递参数
        emit('grow-up', '我已经会自己写代码了!');
      }
      </script>
    • 父组件 (Parent.vue) :在子组件标签上使用@v-on监听事件。

      vue 复制代码
      <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)

      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)

      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暴露方法或属性。

      vue 复制代码
      <template>
        <p>我是一个深藏不露的组件</p>
      </template>
      
      <script setup>
      import { ref } from 'vue';
      
      const doPushUp = () => {
        console.log('正在做俯卧撑...');
      };
      
      // 把doPushUp方法暴露给父组件
      defineExpose({
        doPushUp
      });
      </script>
    • 父组件 (Parent.vue)

      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)

      vue 复制代码
      <template>
        <ParentComponent />
      </template>
      
      <script setup>
      import { provide, ref } from 'vue';
      
      const themeColor = ref('dark');
      // 提供数据,'theme'是暗号
      provide('theme', themeColor); 
      </script>
    • 后代组件 (GrandChild.vue)

      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)

      javascript 复制代码
      import { defineStore } from 'pinia';
      
      export const useUserStore = defineStore('user', {
        state: () => ({
          name: '訾博',
          isLoggedIn: false,
        }),
        actions: {
          login() {
            this.isLoggedIn = true;
          },
        },
      });
    • 在任意组件中使用

      vue 复制代码
      <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)

      javascript 复制代码
      import mitt from 'mitt';
      const emitter = mitt();
      export default emitter;
    • 组件A (发送方)

      vue 复制代码
      <script setup>
      import emitter from '@/utils/emitter';
      
      function sendMessage() {
        emitter.emit('some-event', '来自A组件的问候');
      }
      </script>
    • 组件B (接收方)

      vue 复制代码
      <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-model propsemits的语法糖 ★★★★★
跨代通信 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的组件通信有了"地表最强"的理解。下去多写写代码,把这些"社交礼仪"都实践一遍。

下课!有什么问题,随时再来问老师。

相关推荐
恋猫de小郭12 小时前
Flutter Zero 是什么?它的出现有什么意义?为什么你需要了解下?
android·前端·flutter
崔庆才丨静觅18 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606119 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了19 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅19 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅19 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅20 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment20 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅20 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊20 小时前
jwt介绍
前端