Vue组件通信
Vue2组件通信方式: Props / $emit (父子组件通信) // 父组件 <template> <Child :message="parentMsg" @childEvent="handleEvent" /> </template> <script> export default { data() { return { parentMsg: 'Hello from parent' } }, methods: { handleEvent(data) { console.log('收到子组件事件:', data) } } } </script> // 子组件 <script> export default { props: ['message'], methods: { sendToParent() { this.$emit('childEvent', '数据来自子组件') } } } </script> provide / inject (跨层级传递) //provide/inject 默认不是响应式的,需要传递响应式对象才能实现响应式。 // 祖先组件 export default { provide() { return { userInfo: this.userInfo, updateUser: this.updateUser } }, data() { return { userInfo: { name: '张三' } } }, methods: { updateUser(name) { this.userInfo.name = name } } } // 后代组件 export default { inject: ['userInfo', 'updateUser'], mounted() { console.log(this.userInfo) this.updateUser('李四') } } Event Bus (事件总线) // EventBus 对象本质上就是一个普通的 Vue 实例,没有 template等等,但拥有 Vue 实例的所有能力 // event-bus.js import Vue from 'vue' export const EventBus = new Vue() // 组件 A(发送事件) import { EventBus } from './event-bus' EventBus.$emit('custom-event', { data: 'some data' }) // 组件 B(监听事件) import { EventBus } from './event-bus' EventBus.$on('custom-event', (payload) => { console.log(payload) }) // 监听事件组件销毁前移除监听 beforeDestroy() { EventBus.$off('custom-event') } 为什么 EventBus 能跨组件通信? 因为 A 和 B 导入的是同一个对象(单例模式) 所以它们操作的是同一个事件中心 EventBus 本质上是一个 全局的发布-订阅对象 ,它会持久存在(除非页面刷新或关闭)。当一个组件监听了一个事件后,EventBus 内部会持有该监听函数的引用。 发送事件的组件:只是调用 $emit 触发事件,EventBus 不持有该组件的任何引用,组件销毁不影响 EventBus 监听事件的组件:通过 $on 将回调函数注册到 EventBus 上,EventBus 持有这个回调函数的引用,如果回调函数中访问了组件的数据,就会形成引用链 如果不移除会有什么后果? 内存泄漏 当组件 B 被销毁(如路由切换、v-if 隐藏)后: 组件 B 虽然从 DOM 中移除了 但 EventBus 仍然持有回调函数的引用 回调函数通过闭包持有组件 B 的 this 引用 组件 B 及其所有数据(包括 hugeData)永远不会被垃圾回收 重复监听导致逻辑错误 //用户反复进入/离开组件多次 // 组件 mounted() { EventBus.$on('update-count', () => { this.count++ // 每次进入组件都会执行多次 }) } // 场景:用户进入页面 → 离开 → 重新进入 → 点击按钮触发事件 // 点击一次,count 会增加 3 次(因为注册了 3 个监听器) 组件销毁后仍然执行逻辑 Vuex (全局状态管理) // store.js export default new Vuex.Store({ state: { count: 0 }, mutations: { increment(state) { state.count++ } }, actions: { incrementAsync({ commit }) { setTimeout(() => commit('increment'), 1000) } }, getters: { doubleCount: state => state.count * 2 } }) // 组件中使用 this.$store.commit('increment') this.$store.dispatch('incrementAsync') this.$store.getters.doubleCount Vue3组件通信: Props / $emit (变化较小) <!-- 子组件 Child.vue --> <script setup> // 定义 props const props = defineProps({ message: String, count: Number }) // 定义 emits const emit = defineEmits(['update', 'childEvent']) const sendToParent = () => { emit('childEvent', '数据来自子组件') } </script> <!-- 父组件 --> <template> <Child :message="msg" @childEvent="handleEvent" /> </template> <script setup> import { ref } from 'vue' import Child from './Child.vue' const msg = ref('Hello from parent') const handleEvent = (data) => { console.log(data) } </script> 使用 mitt 实现 Event Bus Vue 3 专注于组件核心功能,EventBus 属于“模式”而非“核心功能” // event-bus.js import mitt from 'mitt' export const emitter = mitt() // 组件 A(发送事件) import { emitter } from './event-bus' emitter.emit('custom-event', { data: 'some data' }) // 组件 B(监听事件) import { emitter } from './event-bus' import { onUnmounted } from 'vue' const handler = (payload) => { console.log(payload) } emitter.on('custom-event', handler) // 组件卸载时移除监听 onUnmounted(() => { emitter.off('custom-event', handler) }) Vuex 4 / Pinia (状态管理) // stores/user.js import { defineStore } from 'pinia' export const useUserStore = defineStore('user', { state: () => ({ name: '张三', age: 18 }), getters: { adult: (state) => state.age >= 18 }, actions: { updateName(name) { this.name = name } } }) // 组件中使用 <script setup> import { useUserStore } from '@/stores/user' import { storeToRefs } from 'pinia' const userStore = useUserStore() // 使用 storeToRefs 保持响应性 const { name, age, adult } = storeToRefs(userStore) const { updateName } = userStore // 直接调用 action updateName('李四') </script>
布莱克2026-05-10 15:49Vue已编辑