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

目录

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

1. Props Down(父传子)

2. Events Up(子传父)

3. ref直接访问

4. v-model双向绑定

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

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

2. Event Bus(事件总线)

3. 全局变量挂载

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

1. Provide/Inject

2. 使用浏览器存储

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

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

五、实战建议

⭐  写在最后


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

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

1. Props Down(父传子)

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

// 父组件
<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触发事件,父组件监听这些事件:

// 子组件
<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直接调用子组件的方法或访问数据:

// 子组件
<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双向绑定

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

// 子组件
<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. 通过共同的父组件中转

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

// 父组件
<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(事件总线)

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

// 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原型上挂载共享对象:

// 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

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

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

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

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

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

2. 使用浏览器存储

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

// 组件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可以创建响应式对象:

// 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

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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

江城开朗的豌豆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值