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

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

相关推荐
快乐巅峰几秒前
为什么选择Elysia.js - Node.js后端框架的最佳选择
前端·后端
A了LONE1 分钟前
自定义btn按钮
java·前端·javascript
梦想CAD控件8 分钟前
在线CAD实现形位公差标注(在线编辑DWG)
前端·javascript·node.js
掘金一周9 分钟前
写个vite插件自动处理系统权限,降低99%重复工作 | 掘金一周 7.17
前端·人工智能·后端
爱编程的喵31 分钟前
React Fragment 深度解析:告别多余的 DOM 节点
前端·react.js
多啦C梦a32 分钟前
《hash+history》你点“关于”,页面却没刷新?!——揭秘前端路由的“穿墙术”
前端·javascript·面试
HHW35 分钟前
大文件上传难题?前端优雅解决方案全解析!
前端·node.js
蓝倾35 分钟前
淘宝获取商品规格接口(item-sku)操作详解
前端·后端·fastapi
水纹36 分钟前
使用pdfjs_3.2.146 预览并本地存储批注demo
前端·javascript
血舞之境37 分钟前
Android Gradle Plugin 7x 升级到 8.1 实战问题总结
前端