Vue 组件通信
Vue 中,组件通信,简单来说就是:父子级组件的数据传递和消息发送,通常分为以下几种情况:
- 父组件向子组件传递数据
- 子组件向父组件发送消息
- 子组件( 兄弟组件 )之间的通信
父子级组件之间的关系,可以总结为:props down,events up;
意思就是:父组件通过 prop 向下传递数据给子组件,子组件通过 events 向上发送消息给父组件。
- 特殊情况( 慎用 ):$refs 和 $parent
( 父 )prop 单向数据流 — ( 子 )组件选项 props
组件实例的作用域是孤立的,这意味着不能( 也不应该 )在子组件的模板内直接引用父组件的数据。
- 父组件通过
单向数据流 prop向下传递数据给子组件,主要分为 静态 prop 和 动态 prop 两种方式// 父组件 <template> <div class="content"> <!-- 方式 1:静态 prop — 通过为子组件在父组件中的占位符添加特性的方式来达到传值的目的 --> <Child hello="具体的值"></Child> // 直接传入具体的值 <!-- 动态 prop — 动态地绑定父组件的数据到子组件的 props,可以使用 v-bind --> <Child :hello="hello"></Child> </div> </template> <script> import Child from "./components/Child.vue"; export default { components: { Child, }, data() { return { hello: "父组件的数据", }; }, }; </script>
- 要让子组件使用父组件的数据,需要通过子组件的 props 选项,显式的声明它期待获得的数据
// 子组件 child.vue <template> <div id="content"> {{hello}} </div> </template> <script> export default{ // 子组件通过 props 选项显式的声明它期待获得的数据 props:['hello'] } </script>
( 子 )使用 $emit 触发自定义事件,并传递数据 — ( 父 )监听子组件事件
子组件中的一些功能,可能需要和父组件进行沟通,这时,可以通过 $emit 触发自定义事件,向父组件传递信息
// 子组件 Child.vue <template> <div id="content"> <button @click="btnClick">按钮</button> </div> </template> <script> export default{ data(){ return{ msg:'子组件数据' } }, methods:{ btnClick(){ // 触发事件$emit(eventName):子组件通过自定义事件,向父组件传递信息 this.$emit('btnCli',this.msg) } } } </script>
// 父组件 app.js <template> <div id="app"> {{hello}} <!-- @eventName 父组件监听子组件的事件,这个事件被触发时,调用dianji方法 --> <Child @btnCli="dianji"></Child> </div> </template> <script> import Child from './components/Child.vue' export default { name: 'App', data(){ return{ hello:'' } }, components: { Child }, methods:{ dianji(data){ // data,即传递过来的子组件数据 this.hello = data; } } } </script>
子组件( 兄弟组件 )之间的通信
事实上,Vue 中并没有针对“兄弟”组件之间通信的方法,可以先把数据传递到父组件中,再通过父组件传递给子组件。
- 选择 1:如果觉得麻烦,也可以使用 Vue 推出的状态管理工具 — Vuex
原理很简单,就是把变量的内容提到最高层级,然后就可以在任意组件中调用了,可以理解为:JS 中的全局变量。
- 选择 2:事件总线 EventBus
在 Vue 中,可以使用 EventBus 作为( 兄弟 )组件之间沟通的桥梁,可以这样理解:
- 所有的组件共用相同的事件中心,可以向该中心注册事件,通过 $emit() 触发并传递数据,
- 然后使用 $on() 监听事件并接收数据( 通常在 mounted() 中 ),这样所有组件都可以上下平行的通知其他组件
但是,若使用不慎,容易造成难以维护的“灾难”( 可以考虑下 $off 移除事件监听 )。
因此,才需要更完善的 Vuex 作为状态管理中心,将通知的概念上升到共享状态层次。
- 初始化事件总线
实质上 EventBus 是一个不具备 DOM 的组件,它具有的仅仅只是它的实例方法而已,因此,非常的轻便。
# 方式 1:局部的事件总线 // event-bus.js import Vue from 'vue' export const EventBus = new Vue() // 在组件中使用 import { EventBus } from "../event-bus.js" // 引入 EventBus.$emit("aMsg", '来自A页面的消息') // 触发 EventBus.$on("aMsg", (msg) => { // 监听 // A发送来的消息 this.msg = msg; }) # 方式 2:初始化为一个全局的事件总线 // main.js Vue.prototype.$EventBus = new Vue()
- 注册事件的触发、监听和销毁( 以下基于方式 2:全局的事件总线,简单一些 )
// 父组件 <template> <div id="app"> <Child1></Child1> <Child2></Child2> <button @click="handle">移除EventBus</button> </div> </template> <script> import Child1 from './components/Child1' import Child2 from './components/Child2' export default { components:{ Child1,Child2 }, methods:{ handle(){ // 移除对某个事件的监听 // this.$EventBus.$off('Child1Msg') // 移除所有的事件总线 this.$EventBus.$off() } } } </script>// components/Child1/index.vue <template> <div class="content"> <div>Child1组件{{num}}</div> <button @click="handle">点击</button> </div> </template> <script> export default { data(){ return { num:0 } }, mounted(){ this.$EventBus.$on('Child2Msg', val => { this.num += val; }) }, methods:{ handle(){ // 触发一个名叫‘Child1Msg’的事件,并传递数据 this.$EventBus.$emit('Child1Msg',1) } } } </script>// components/Child2/index.vue <template> <div class="content"> <div>Child2组件{{ num }}</div> <button @click="handle">点击</button> </div> </template> <script> export default { data() { return { num: 0, }; }, mounted() { // 监听事件‘Child1Msg’ this.$EventBus.$on("Child1Msg", (val) => { this.num += val; }); }, methods: { handle() { // 触发‘Child2Msg’事件,并传递数据 this.$EventBus.$emit("Child2Msg", 2); }, }, }; </script>
