Vue组件通信的N种姿势:不用Vuex也能玩转兄弟和父子组件!

大家好,我是小杨,一个摸爬滚打了6年的前端老油条。今天咱们来聊聊Vue组件通信这个经典话题------在不使用Vuex的情况下,如何优雅地实现父子组件、兄弟组件之间的数据传递?相信我,掌握这些方法后,你会发现Vuex并不是唯一的选择。

一、父子组件通信的4种常规武器

1. Props Down(父传子)

这是最基础的通信方式,父组件通过props向子组件传递数据:

javascript 复制代码
// 父组件
<template>
  <child-component :message="parentMessage" />
</template>

<script>
export default {
  data() {
    return {
      parentMessage: '我是父组件传来的消息'
    }
  }
}
</script>

// 子组件
<script>
export default {
  props: {
    message: {
      type: String,
      default: ''
    }
  },
  mounted() {
    console.log(this.message) // 输出:我是父组件传来的消息
  }
}
</script>

适用场景:简单的单向数据流,父组件控制子组件显示内容

2. Events Up(子传父)

子组件通过$emit触发事件,父组件监听这些事件:

javascript 复制代码
// 子组件
<template>
  <button @click="sendMessage">点我传消息</button>
</template>

<script>
export default {
  methods: {
    sendMessage() {
      this.$emit('message-from-child', '我是子组件的问候')
    }
  }
}
</script>

// 父组件
<template>
  <child-component @message-from-child="handleMessage" />
</template>

<script>
export default {
  methods: {
    handleMessage(msg) {
      console.log(msg) // 输出:我是子组件的问候
    }
  }
}
</script>

适用场景:子组件需要通知父组件某些操作或状态变化

3. ref直接访问

父组件通过ref直接调用子组件的方法或访问数据:

javascript 复制代码
// 子组件
<script>
export default {
  data() {
    return {
      childData: '我是子组件的数据'
    }
  },
  methods: {
    showMessage() {
      console.log('子组件方法被调用')
    }
  }
}
</script>

// 父组件
<template>
  <child-component ref="myChild" />
</template>

<script>
export default {
  mounted() {
    console.log(this.$refs.myChild.childData) // 输出:我是子组件的数据
    this.$refs.myChild.showMessage() // 输出:子组件方法被调用
  }
}
</script>

适用场景:需要直接操作子组件时(慎用,容易造成紧耦合)

4. v-model双向绑定

实现父子组件的双向数据绑定:

javascript 复制代码
// 子组件
<template>
  <input :value="value" @input="$emit('input', $event.target.value)" />
</template>

<script>
export default {
  props: ['value']
}
</script>

// 父组件
<template>
  <child-component v-model="parentValue" />
</template>

<script>
export default {
  data() {
    return {
      parentValue: ''
    }
  }
}
</script>

适用场景:表单控件等需要双向绑定的场景

二、兄弟组件通信的3种骚操作

1. 通过共同的父组件中转

这是最正统的兄弟组件通信方式:

javascript 复制代码
// 父组件
<template>
  <child-a @change="handleChange" />
  <child-b :message="sharedMessage" />
</template>

<script>
export default {
  data() {
    return {
      sharedMessage: ''
    }
  },
  methods: {
    handleChange(msg) {
      this.sharedMessage = msg
    }
  }
}
</script>

// ChildA组件
<script>
export default {
  methods: {
    sendMessage() {
      this.$emit('change', '来自A组件的消息')
    }
  }
}
</script>

// ChildB组件
<script>
export default {
  props: ['message'],
  watch: {
    message(newVal) {
      console.log(newVal) // 输出:来自A组件的消息
    }
  }
}
</script>

适用场景:兄弟组件有共同父组件且层级不深的情况

2. Event Bus(事件总线)

创建一个中央事件总线来实现任意组件间通信:

javascript 复制代码
// eventBus.js
import Vue from 'vue'
export const EventBus = new Vue()

// 组件A(发送事件)
<script>
import { EventBus } from './eventBus'
export default {
  methods: {
    sendMessage() {
      EventBus.$emit('my-event', '我是组件A的消息')
    }
  }
}
</script>

// 组件B(接收事件)
<script>
import { EventBus } from './eventBus'
export default {
  created() {
    EventBus.$on('my-event', msg => {
      console.log(msg) // 输出:我是组件A的消息
    })
  },
  beforeDestroy() {
    // 记得移除监听,避免内存泄漏
    EventBus.$off('my-event')
  }
}
</script>

适用场景:任意组件间通信,特别是没有直接关系的组件

坑点提示:一定要记得在组件销毁时移除事件监听,否则会导致内存泄漏!

3. 全局变量挂载

在Vue原型上挂载共享对象:

javascript 复制代码
// main.js
Vue.prototype.$shared = {
  data: '',
  updateData(newData) {
    this.data = newData
  }
}

// 组件A
<script>
export default {
  methods: {
    updateSharedData() {
      this.$shared.updateData('新的共享数据')
    }
  }
}
</script>

// 组件B
<script>
export default {
  created() {
    console.log(this.$shared.data) // 输出:新的共享数据
  }
}
</script>

适用场景:简单的全局状态共享

注意事项:这种方法不够响应式,需要手动触发更新

三、进阶通信方案(适合复杂场景)

1. Provide/Inject

实现祖先组件向后代组件传值(跨多级传递):

javascript 复制代码
// 祖先组件
<script>
export default {
  provide() {
    return {
      ancestorData: '我是祖先的数据'
    }
  }
}
</script>

// 后代组件(任意层级)
<script>
export default {
  inject: ['ancestorData'],
  created() {
    console.log(this.ancestorData) // 输出:我是祖先的数据
  }
}
</script>

适用场景:深层嵌套组件通信

限制:provide/inject绑定不是响应式的(除非传递一个响应式对象)

2. 使用浏览器存储

通过localStorage或sessionStorage实现组件间通信:

javascript 复制代码
// 组件A
<script>
export default {
  methods: {
    saveData() {
      localStorage.setItem('sharedKey', JSON.stringify({ msg: '跨组件消息' }))
    }
  }
}
</script>

// 组件B
<script>
export default {
  created() {
    window.addEventListener('storage', this.handleStorageChange)
  },
  methods: {
    handleStorageChange(e) {
      if (e.key === 'sharedKey') {
        const data = JSON.parse(e.newValue)
        console.log(data.msg) // 输出:跨组件消息
      }
    }
  },
  beforeDestroy() {
    window.removeEventListener('storage', this.handleStorageChange)
  }
}
</script>

适用场景:跨标签页通信或数据持久化

3. 使用Observable实现简易状态管理

Vue.observable可以创建响应式对象:

javascript 复制代码
// sharedState.js
import Vue from 'vue'
export const state = Vue.observable({
  count: 0
})

export const mutations = {
  increment() {
    state.count++
  }
}

// 组件A
<script>
import { state, mutations } from './sharedState'
export default {
  methods: {
    incrementCount() {
      mutations.increment()
    }
  }
}
</script>

// 组件B
<script>
import { state } from './sharedState'
export default {
  computed: {
    count() {
      return state.count
    }
  }
}
</script>

适用场景:中小型应用的状态管理

四、如何选择合适的通信方式?

经过多年踩坑,我总结出以下选择原则:

  1. 父子组件

    • 简单数据传递:props down / events up
    • 需要双向绑定:v-model
    • 需要直接调用方法:ref(慎用)
  2. 兄弟组件

    • 有共同父组件:通过父组件中转
    • 无直接关系:Event Bus或全局状态
  3. 跨多级组件

    • provide/inject
    • 全局状态管理
  4. 需要持久化

    • 浏览器存储方案

五、实战建议

  1. 避免过度设计:简单场景就用props和events,别上来就搞复杂方案
  2. 注意内存泄漏:使用Event Bus一定要记得$off
  3. 保持单向数据流:尽量让数据流向可预测
  4. 考虑可维护性:选择团队熟悉的方案,而不是最"高级"的方案

记住,没有最好的方案,只有最适合的方案。根据你的具体场景选择最合适的通信方式,而不是盲目追求技术的新颖性。

⭐ 写在最后

请大家不吝赐教,在下方评论或者私信我,十分感谢🙏🙏🙏.

✅ 认为我某个部分的设计过于繁琐,有更加简单或者更高逼格的封装方式

✅ 认为我部分代码过于老旧,可以提供新的API或最新语法

✅ 对于文章中部分内容不理解

✅ 解答我文章中一些疑问

✅ 认为某些交互,功能需要优化,发现BUG

✅ 想要添加新功能,对于整体的设计,外观有更好的建议

✅ 一起探讨技术加qq交流群:906392632

最后感谢各位的耐心观看,既然都到这了,点个 👍赞再走吧!

相关推荐
一个专注api接口开发的小白20 分钟前
Python/Node.js 调用taobao API:构建实时商品详情数据采集服务
前端·数据挖掘·api
掘金一周27 分钟前
我开源了一款 Canvas “瑞士军刀”,十几种“特效与工具”开箱即用 | 掘金一周 8.14
前端·人工智能·后端
嘘不要声张37 分钟前
地图点聚合(谷歌)
前端
缉毒英雄祁同伟43 分钟前
企业级WEB应用服务器TOMCAT
java·前端·tomcat
har01d43 分钟前
在 uniapp 里使用 unocss,vue3 + vite 项目
前端·uni-app·vue·uniapp·unocss
OLong1 小时前
React Update Queue 源码全链路解析:从 setState 到 DOM 更新
前端·react.js
知识浅谈1 小时前
OpenLayers与Vue.js结合实现前端地图应用
前端
paopaokaka_luck1 小时前
校园快递小程序(腾讯地图API、二维码识别、Echarts图形化分析)
vue.js·spring boot·后端·小程序·uni-app
掘金011 小时前
Vue3 项目中实现特定页面离开提示保存功能方案
javascript·vue.js
答案answer1 小时前
three.js 实现几个好看的文本内容效果
前端·webgl·three.js