Vue3的组件通信方式

通信方式 适用层级 数据流向 复杂度
Props/Emits 父子组件 单向/双向 ★☆☆
v-model 父子组件 双向 ★☆☆
Provide/Inject 跨层级组件 自上而下 ★★☆
事件总线 任意组件 任意方向 ★★★
Pinia/Vuex 全局状态 任意方向 ★★☆
Refs模板引用 父子组件 父→子 ★☆☆
作用域插槽 父子组件 子→父 ★★☆
Web Workers 跨线程通信 任意方向 ★★★★

1. Props/Emits:基础父子通信

适用场景 :直接父子组件通信
注意:避免直接修改props,使用emit通知父组件修改

javascript 复制代码
<!-- 父组件 -->
<script setup>
import Child from './Child.vue';
const message = ref('父组件数据');
const handleEmit = (data) => {
  console.log('子组件传递:', data);
};
</script>

<template>
  <Child :msg="message" @child-event="handleEmit" />
</template>

<!-- 子组件 Child.vue -->
<script setup>
const props = defineProps(['msg']);
const emit = defineEmits(['child-event']);

const sendToParent = () => {
  emit('child-event', { time: new Date() });
};
</script>

<template>
  <div>收到: {{ msg }}</div>
  <button @click="sendToParent">发送事件</button>
</template>
2. v-model 双向绑定升级

优势 :替代Vue2的.sync修饰符,语法更简洁
原理 :相当于 :modelValue + @update:modelValue

javascript 复制代码
<!-- 父组件 -->
<script setup>
import CustomInput from './CustomInput.vue';
const username = ref('');
</script>

<template>
  <CustomInput v-model="username" />
</template>

<!-- 子组件 CustomInput.vue -->
<script setup>
const model = defineModel();
</script>

<template>
  <input 
    type="text"
    :value="model"
    @input="model = $event.target.value"
  />
</template>
3. Provide/Inject 跨层级通信

适用场景 :多级嵌套组件共享数据
注意:避免滥用,复杂场景建议用状态管理

javascript 复制代码
// 祖先组件
<script setup>
import { provide, ref } from 'vue';
const theme = ref('dark');
provide('app-theme', {
  theme,
  toggle: () => {
    theme.value = theme.value === 'dark' ? 'light' : 'dark';
  }
});
</script>

// 任意后代组件
<script setup>
import { inject } from 'vue';
const { theme, toggle } = inject('app-theme');
</script>

<template>
  <button @click="toggle">
    当前主题: {{ theme }}
  </button>
</template>
4. 事件总线替代方案(mitt)

适用场景 :非父子组件通信
优势 :轻量级(仅200B),替代Vue2的$emit/$on

javascript 复制代码
// eventBus.js
import mitt from 'mitt';
export default mitt();

// 组件A(发布事件)
import bus from './eventBus';
bus.emit('user-login', { user: 'admin' });

// 组件B(订阅事件)
import bus from './eventBus';
bus.on('user-login', (userData) => {
  console.log('用户登录:', userData);
});

// 组件卸载时取消订阅
onUnmounted(() => {
  bus.off('user-login');
});
5. pinia状态管理(推荐)

优势 :类型安全、Devtools支持、模块化设计
对比Vuex:更简洁API,去除mutations概念

javascript 复制代码
// stores/user.js
import { defineStore } from 'pinia';
export const useUserStore = defineStore('user', {
  state: () => ({ name: '', isLogin: false }),
  actions: {
    login(name) {
      this.name = name;
      this.isLogin = true;
    }
  }
});

// 组件中使用
<script setup>
import { useUserStore } from '@/stores/user';
const userStore = useUserStore();

const login = () => {
  userStore.login('张三');
};
</script>

<template>
  <div>用户名: {{ userStore.name }}</div>
</template>
6. 模板引用通信

适用场景 :父组件需要直接访问子组件方法或数据
限制:只能在父子组件间使用

javascript 复制代码
<!-- 父组件 -->
<script setup>
import Child from './Child.vue';
import { ref, onMounted } from 'vue';

const childRef = ref(null);

onMounted(() => {
  // 调用子组件方法
  childRef.value?.childMethod();
  
  // 访问子组件数据
  console.log(childRef.value?.childData);
});
</script>

<template>
  <Child ref="childRef" />
</template>

<!-- 子组件 Child.vue -->
<script setup>
import { defineExpose } from 'vue';

const childData = ref('子组件数据');
const childMethod = () => {
  console.log('子组件方法被调用');
};

// 暴露给父组件
defineExpose({
  childData,
  childMethod
});
</script>
7. 作用域插槽(子→父通信)

适用场景 :子组件需要向父组件传递渲染内容
优势:保持子组件封装性的同时提供定制能力

javascript 复制代码
<!-- 子组件 ScopedList.vue -->
<script setup>
const items = ref(['Vue', 'React', 'Angular']);
</script>

<template>
  <ul>
    <li v-for="(item, index) in items" :key="index">
      <slot :item="item" :index="index" />
    </li>
  </ul>
</template>

<!-- 父组件 -->
<script setup>
import ScopedList from './ScopedList.vue';
</script>

<template>
  <ScopedList v-slot="{ item, index }">
    <span :class="{ active: index === 0 }">{{ item }}</span>
  </ScopedList>
</template>
8. Web Workers 跨线程通信

适用场景 :CPU密集型任务,避免阻塞UI线程
注意:worker中无法访问DOM

javascript 复制代码
// worker.js
self.onmessage = (e) => {
  const result = heavyCalculation(e.data);
  self.postMessage(result);
};

function heavyCalculation(data) {
  // 复杂计算逻辑
  return data * 2;
}

// 组件中使用
<script setup>
import { ref } from 'vue';

const worker = new Worker('./worker.js');
const result = ref(0);

worker.onmessage = (e) => {
  result.value = e.data;
};

const startCalc = () => {
  worker.postMessage(1000000); // 发送大数据
};
</script>

<template>
  <button @click="startCalc">开始计算</button>
  <div>结果: {{ result }}</div>
</template>

通信方式对比指南

通信方式 适用场景 优点 缺点
Props/Emits 父子组件简单通信 简单直接 多层传递繁琐
v-model 表单组件双向绑定 语法简洁 仅适用特定场景
Provide/Inject 跨层级组件共享 避免逐层传递 数据来源不透明
事件总线 任意组件间事件通知 灵活解耦 难以跟踪调试
Pinia/Vuex 全局状态管理 集中管理,调试友好 学习成本较高
模板引用 父组件访问子组件内部 精确访问 破坏组件封装性
作用域插槽 子组件向父组件暴露渲染数据 灵活定制UI 仅适用于模板内容
Web Workers 后台计算任务 避免UI阻塞 通信成本高

实战选型建议

  1. 父子组件 :优先使用 props/emits + v-model

  2. 兄弟组件 :采用共享父组件状态 或 事件总线

  3. 祖孙组件 :使用 provide/inject

  4. 复杂应用Pinia 管理全局状态

  5. 性能敏感Web Workers 处理计算密集型任务

  6. UI定制作用域插槽 实现内容分发

相关推荐
崔庆才丨静觅13 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606113 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了14 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅14 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅14 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅14 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment14 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅15 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊15 小时前
jwt介绍
前端
爱敲代码的小鱼15 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax