在 Vue 3 中,组件之间的通信是构建应用程序的关键。本文将介绍 13 种不同的组件通信方法,从最简单到最复杂,帮助你选择最适合你需求的方式。
-
父组件向子组件传递数据 (Props)
这是最基本也是最常用的通信方式。父组件通过属性向子组件传递数据。
「父组件:」
<template> <child :name="name"></child> </template> <script setup> import { ref } from 'vue' import Child from './Child.vue' const name = ref('小明') </script>
「子组件:」
<template> <div>{{ props.name }}</div> </template> <script setup> import { defineProps } from 'vue' const props = defineProps({ name: { type: String, default: '', }, }) </script>
-
子组件向父组件传递数据 (Emit)
子组件可以通过触发事件的方式向父组件传递数据。
「子组件:」
<template> <button @click="handleClick">点击我</button> </template> <script setup> import { ref, defineEmits } from 'vue' const message = ref('来自子组件的问候') const emits = defineEmits(['greet']) const handleClick = () => { emits('greet', message.value) } </script>
「父组件:」
<template> <child @greet="handleGreet"></child> </template> <script setup> import { ref } from 'vue' import Child from './Child.vue' const handleGreet = (message) => { console.log(message) // 输出: "来自子组件的问候" } </script>
-
兄弟组件通信 (Mitt)
对于兄弟组件之间的通信,我们可以使用第三方库 mitt 来实现一个简单的事件总线。
首先,安装 mitt:
npm install --save mitt
然后,在 main.js 中全局配置:
import { createApp } from 'vue' import mitt from 'mitt' import App from './App.vue' const app = createApp(App) app.config.globalProperties.$bus = mitt() app.mount('#app')
「发送事件的组件:」
<script setup> import { getCurrentInstance } from 'vue' const { proxy } = getCurrentInstance() const sendMessage = () => { proxy.$bus.emit('myEvent', '你好,兄弟') } </script>
「接收事件的组件:」
<script setup> import { onMounted, getCurrentInstance } from 'vue' const { proxy } = getCurrentInstance() onMounted(() => { proxy.$bus.on('myEvent', (message) => { console.log(message) // 输出: "你好,兄弟" }) }) </script>
-
透传 Attributes ($attrs)
$attrs 包含了父组件传递给子组件的所有属性,除了那些已经被 props 或 emits 声明的。
「父组件:」
<template> <child name="小明" age="18" hobby="篮球"></child> </template>
「子组件:」
<script setup> import { useAttrs } from 'vue' const attrs = useAttrs() console.log(attrs) // { age: "18", hobby: "篮球" } </script>
-
模板引用 (Refs)
通过 ref,父组件可以直接访问子组件的属性和方法。
「父组件:」
<template> <child ref="childRef"></child> <button @click="callChildMethod">调用子组件方法</button> </template> <script setup> import { ref } from 'vue' import Child from './Child.vue' const childRef = ref(null) const callChildMethod = () => { childRef.value.someMethod() } </script>
「子组件:」
<script setup> import { defineExpose } from 'vue' const someMethod = () => { console.log('子组件方法被调用了') } defineExpose({ someMethod, }) </script>
-
双向绑定 (v-model)
v-model 提供了一种简洁的方式来实现父子组件之间的双向数据绑定。
「父组件:」
<template> <child v-model:name="name"></child> </template> <script setup> import { ref } from 'vue' import Child from './Child.vue' const name = ref('小明') </script>
「子组件:」
<template> <input :value="name" @input="updateName" /> </template> <script setup> import { defineProps, defineEmits } from 'vue' const props = defineProps(['name']) const emit = defineEmits(['update:name']) const updateName = (e) => { emit('update:name', e.target.value) } </script>
-
依赖注入 (Provide/Inject)
provide 和 inject 允许祖先组件向所有子孙组件传递数据,而不需要通过每一层组件手动传递。
「祖先组件:」
<script setup> import { provide, ref } from 'vue' const themeColor = ref('blue') provide('theme', themeColor) </script>
「子孙组件:」
<script setup> import { inject } from 'vue' const theme = inject('theme') console.log(theme.value) // 'blue' </script>
-
路由传参
Vue Router 提供了多种方式在路由之间传递参数。
「通过query传参:」
import { useRouter } from 'vue-router' const router = useRouter() router.push({ path: '/user', query: { id: 123 } }) // 在目标组件中 import { useRoute } from 'vue-router' const route = useRoute() console.log(route.query.id) // 123
-
Vuex 状态管理
Vuex 是 Vue 的官方状态管理库,适用于大型应用。
// store/index.js import { createStore } from 'vuex' export default createStore({ state: { count: 0, }, mutations: { increment(state) { state.count++ }, }, }) // 在组件中使用 import { useStore } from 'vuex' const store = useStore() console.log(store.state.count) store.commit('increment')
-
Pinia 状态管理
Pinia 是新一代的 Vue 状态管理库,提供更简单的 API 和更好的 TypeScript 支持。
// stores/counter.js import { defineStore } from 'pinia' export const useCounterStore = defineStore('counter', { state: () => ({ count: 0 }), actions: { increment() { this.count++ }, }, }) // 在组件中使用 import { useCounterStore } from '@/stores/counter' const counter = useCounterStore() console.log(counter.count) counter.increment()
-
浏览器存储
localStorage 和 sessionStorage 可以用于在不同页面或组件之间共享数据。
// 存储数据 localStorage.setItem('user', JSON.stringify({ name: '小明', age: 18 })) // 读取数据 const user = JSON.parse(localStorage.getItem('user'))
-
Window 对象
虽然不推荐,但在某些场景下,可以使用 window 对象在全局范围内共享数据。
// 设置全局数据 window.globalData = { message: '全局消息' } // 在任何地方使用 console.log(window.globalData.message)
-
全局属性
Vue 3 提供了 app.config.globalProperties 来替代 Vue 2 中的 Vue.prototype,用于添加全局可用的属性。
// main.js const app = createApp(App) app.config.globalProperties.$http = axios // 在组件中使用 import { getCurrentInstance } from 'vue' const { proxy } = getCurrentInstance() proxy.$http.get('/api/data')
总结
这 13 种方法涵盖了 Vue 3 中几乎所有的组件通信场景。根据你的具体需求和应用规模,选择最合适的通信方式。记住,好的组件设计能够简化通信,提高代码的可维护性。