Vue3实现组件通信的14种方式详解与实战
作者:编程随想_Code
一、父子组件通信
1. props 父传子
适用场景:父组件向子组件传递数据
核心用法:defineProps 接收 props
<!-- 父组件 Parent.vue -->
<template>
<Child :message="parentMsg" />
</template>
<script setup>
import { ref } from 'vue';
import Child from './Child.vue';
const parentMsg = ref('Hello from Parent');
</script>
<!-- 子组件 Child.vue -->
<template>
<div>父组件传递的props: {{ message }}</div>
</template>
<script setup>
const props = defineProps({
message: String
});
</script>注意事项:
- 保持单向数据流,子组件不要直接修改 props。
- 可通过
defineProps设置类型校验和默认值。
2. defineEmits 子传父
适用场景:子组件向父组件传递数据
核心用法:defineEmits 定义自定义事件
<!-- 子组件 Child.vue -->
<template>
<button @click="sendData">发送数据</button>
</template>
<script setup>
const emit = defineEmits(['update']);
function sendData() {
emit('update', 'Data from Child');
}
</script>
<!-- 父组件 Parent.vue -->
<template>
<Child @update="handleUpdate" />
</template>
<script setup>
function handleUpdate(data) {
console.log('接收到子组件数据:', data);
}
</script>补充:
- 推荐使用
update:propName命名规范(如update:count)。 - 避免直接传递父组件方法给子组件调用(如
<Child :onUpdate="handleUpdate" />)。
3. $attrs 属性透传
适用场景:透传未声明的属性到子组件
核心用法:$attrs 自动继承父组件未声明的属性
<!-- 父组件 Parent.vue -->
<template>
<Child :customAttr="123" />
</template>
<!-- 子组件 Child.vue -->
<template>
<div v-attrs="$attrs"></div>
</template>
<script setup>
import { useAttrs } from 'vue';
const attrs = useAttrs();
console.log(attrs.customAttr); // 输出 123
</script>4. ref + defineExpose
适用场景:父组件调用子组件方法或访问子组件数据
核心用法:ref 获取子组件实例,defineExpose 暴露方法/数据
<!-- 父组件 Parent.vue -->
<template>
<Child ref="childRef" />
<button @click="callChildMethod">调用子组件方法</button>
</template>
<script setup>
import { ref } from 'vue';
import Child from './Child.vue';
const childRef = ref(null);
function callChildMethod() {
childRef.value?.childMethod();
}
</script>
<!-- 子组件 Child.vue -->
<script setup>
function childMethod() {
console.log('子组件方法被调用');
}
defineExpose({ childMethod });
</script>注意事项:
- 仅在必要时使用(如表单验证),避免过度依赖。
5. v-model 双向绑定
适用场景:实现父子组件双向数据绑定
核心用法:modelValue + update:modelValue
<!-- 父组件 Parent.vue -->
<template>
<Child v-model="value" />
</template>
<script setup>
import { ref } from 'vue';
const value = ref('初始值');
</script>
<!-- 子组件 Child.vue -->
<template>
<input :value="modelValue" @input="updateValue" />
</template>
<script setup>
const props = defineProps(['modelValue']);
const emit = defineEmits(['update:modelValue']);
function updateValue(e) {
emit('update:modelValue', e.target.value);
}
</script>二、兄弟组件通信
6. 父组件中转
适用场景:兄弟组件通过父组件共享数据
<!-- 父组件 Parent.vue -->
<template>
<BrotherA @data-change="handleDataChange" />
<BrotherB :shared-data="sharedData" />
</template>
<script setup>
import { ref } from 'vue';
const sharedData = ref('');
function handleDataChange(data) {
sharedData.value = data;
}
</script>7. mitt 事件总线
适用场景:轻量级解耦通信(无共同父组件)
核心用法:mitt 创建事件中心
// eventBus.js
import mitt from 'mitt';
export const emitter = mitt();
// 组件A
<script setup>
import { emitter } from './eventBus.js';
function sendData() {
emitter.emit('brother-event', { id: 1 });
}
</script>
// 组件B
<script setup>
import { emitter } from './eventBus.js';
emitter.on('brother-event', (data) => {
console.log('接收到数据:', data);
});
</script>8. 全局状态管理(Pinia/Vuex)
适用场景:复杂应用中的全局状态共享
// store/counter.js (Pinia)
import { defineStore } from 'pinia';
export const useCounterStore = defineStore('counter', {
state: () => ({ count: 0 }),
actions: { increment() { this.count++; } }
});
// 组件A
<script setup>
import { useCounterStore } from '@/store/counter';
const store = useCounterStore();
store.increment();
</script>
// 组件B
<script setup>
import { useCounterStore } from '@/store/counter';
const store = useCounterStore();
console.log(store.count);
</script>三、跨层级通信
9. provide/inject
适用场景:祖孙组件或深层嵌套组件通信
<!-- 父组件 Parent.vue -->
<script setup>
import { provide, ref } from 'vue';
const theme = ref('dark');
provide('theme', theme);
</script>
<!-- 子组件 Child.vue -->
<script setup>
import { inject } from 'vue';
const theme = inject('theme');
console.log('当前主题:', theme.value);
</script>10. 全局状态管理(Pinia/Vuex)
与兄弟组件通信中的 Pinia 用法一致,适用于跨层级状态共享。
四、其他通信方式
11. 浏览器本地存储(localStorage/sessionStorage)
适用场景:持久化数据共享(如用户偏好设置)
// 存储数据
localStorage.setItem('user', JSON.stringify({ name: '豆豆' }));
// 读取数据
const user = JSON.parse(localStorage.getItem('user'));
console.log(user.name); // 输出 豆豆12. 全局 window 对象
适用场景:临时跨组件通信(慎用)
12. 全局 window 对象 适用场景:临时跨组件通信(慎用)
13. ES6 模块化(import/export)
适用场景:常量或工具函数共享
// constants.js
export const API_URL = 'https://api.example.com';
// 组件中使用
import { API_URL } from '@/constants';
console.log(API_URL); // 输出 https://api.example.com14. 作用域插槽(Slot Props)
适用场景:父组件向子组件传递函数或配置对象
<!-- 父组件 Parent.vue -->
<template>
<Child>
<template #default="{ config }">
<div :style="config">{{ config.title }}</div>
</template>
</Child>
</template>
<!-- 子组件 Child.vue -->
<script setup>
import { reactive } from 'vue';
const config = reactive({ title: 'Slot Injected Config' });
</script>五、最佳实践建议
优先选择标准化方案
80% 场景使用 props + emits。
复杂场景选择 provide/inject 或状态管理库。
避免过度使用全局状态
仅当数据需要在多组件间共享时使用 Pinia/Vuex。
善用 $attrs
减少子组件 props 定义,实现属性透传。
谨慎操作 ref
仅在必要时直接调用子组件方法,保持组件解耦。
事件命名规范
使用 on[EventName] 格式,如 on:update。
六、总结
Vue3 提供了丰富且灵活的组件通信方式,开发者应根据具体场景选择最优方案。以下是常见场景的推荐选择:
| 场景 | 推荐通信方式 |
|---|---|
| 父子通信 | props + defineEmits |
| 兄弟组件通信 | mitt 或父组件中转 |
| 跨层级通信 | provide/inject 或 Pinia |
| 全局状态共享 | Pinia 或 Vuex |
| 临时数据共享 | localStorage 或 window |
掌握这些通信方式后,可以更高效地构建复杂应用。建议将示例代码复制到项目中运行,观察不同方式的数据流向和效果差异。
以上就是Vue3实现组件通信的14种方式详解与实战的详细内容,更多关于Vue3组件通信方式的资料请关注脚本之家其它相关文章!
