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.com
14. 作用域插槽(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组件通信方式的资料请关注脚本之家其它相关文章!