Vue提高06 组件间通信
Vue中组件通信的方法。
父子组件通信
父组件向子组件传递数据
父组件向子组件传递数据,直接使用prop即可,父组件中在子组件的实例上通过v-bind传入prop:
1 | <LeftChild :message="myMessage"></LeftChild> |
在子组件中声明了这个prop之后就可以使用:
1 | <template> |
子组件向父组件传递数据
子组件向父组件可以直接通过$emit组件上的事件来进行通信,例如在父组件中,通过v-on为子组件传入一个事件:
1 | <template> |
子组件中通过#emit来触发传入的自定义事件,以参数的形式将数据传递给父组件:
1 | <template> |
非父子组件通信
非父子组件的通信有几种解决方案,如果是比较复杂的应用,可以直接使用Vuex来实现通信和数据管理,如果不使用Vuex则可以使用下面几种方法来实现组件通信。
事件总线
原理就是通过一个空的Vue实例eventBus,A组件首先在eventBus上通过$on订阅一个事件,然后在组件B中通过$emit发布一个事件,在事件中将数据进行传递。
首先在一个单独的文件中定义名称为eventBus的Vue实例,并导出这个实例
1 | // eventBus.js |
在组件A引入eventBus后,在eventBus上订阅一个事件getValue,最好在组件销毁前注销监听的事件:
1 | // 组件A |
然后在组件B中也引入eventBus后,借由eventBus发布getValue事件,并将数据作为第二个参数传递给事件的订阅者:
1 | // 组件B |
事件总线的优化
上面这种方式,在每个需要通信的组件都需要手动引入eventBus,很麻烦。所以希望能够做到一次注入,到处使用。
改进的方法就是在main.js中定义Vue根实例时,将eventBus添加到根实例的data中,然后再每个组件中都可以通过this.#root.eventBus来访问它:
1 | // main.js |
在组件A使用$root.eventBus.$on来订阅事件:
1 | // 组件A |
在组件B中使用$root.eventBus.$on来发布事件:
1 | // 组件B |
通过原型
Vue对象本质上就是一个JS对象,想要引入eventBus只需要在Vue的原型prototype上增加一个属性就可以了。本质上所有的Vue组件都是继承全局的Vue,所以只要在初始化Vue对象之前在prototype上定义属性,这样所有的组件都可以访问这个属性了。
所以在main.js中,在实例化Vue之前增加代码
1 | // main.js |
组件A中
1 | // 组件A |
组件B中
1 | // 组件B |
使用Vue.observale
Vue的2.6版本新增了一个Vue.observale,它可以定义一个可响应的对象,实际上Vue内部会用它来处理data函数返回的对象。
这个API返回的对象可以直接用于渲染函数和计算属性内,并且在发生改变时触发相应的更新,可以作为最小化的跨组件状态存储器,用于简单的场景。
我们新建一个simpleStore.js,导出一个使用Vue.observale处理后的对象:
1 | // simpleStore.js |
然后再main.js中作为Vue的根实例的data的属性导入(也可以根据组件通信的范围,分别在不同的组件导入,比如作为组件的数据处理层时):
1 | // main.js |
这样在需要通信的组件中就可以直接修改这个变量,在另外一个组件中通过计算属性引入这个变量,就可以实现响应式的更新:
组件A中直接修改这个变量:
1 | // 组件A |
组件B中通过计算属性引入这个变量:
1 | // 组件B |
实际上在simpleStore.js中我们可以继续定义commit、action、mutation、dispatch等事件,来统一管理变量的修改,实际上就是自己实现了一个简易的Vuex
在使用Vue.observable时,有两点需要注意:
(1)Vue.observable返回的对象只能用于渲染函数或者计算属性内,才能实现响应式的更新,不能直接用于data函数中赋值
1 | const state = Vue.observable({ count: 0 }) |
(2)在Vue 2.x中,被传入的对象会直接被Vue.observable改变,它和被返回的对象是同一个对象。但是在Vue 3.x中,则会返回一个可响应的代理,而对源对象直接进行修改仍是不可相应的。因此为了兼容性,应该始终操作Vue.observable返回的对象,而不是传入的源对象。