12全局事件总线、消息订阅与发布

1、全局事件总线(GlobalEventBus)

不是新的API

是程序员在开发过程中总结的经验

实现了各个组件之间的通信

1.1 设计思路

1) 需求

假设D组件要传送数据给A组件

子传数据给父,回调函数在父中

QQ截图20220208213323

2) 对x的要求

必须得保证每个组件都能看得到它

x身上必须有$on $off $emit

3)开始设计

需要App组件以及它的所有子组件都要看得到,那么就找到App组件的上一级 ==> main.js

//引入Vue
import Vue from 'vue'
//引入App
import App from './App.vue'
//关闭Vue的生产提示
Vue.config.productionTip = false

// 设计思想一
window.x = {a:1,b:2}  满足条件一,不满足条件二
// 设计思想二
VueComponent.prototype.x = {a:1,b:2}   会出现报错

//创建vm
new Vue({
	el:'#app',
	render: h => h(App),
})
  • 设计思想二为什么会出现报错,该文件中的vue是通过导入进来的库中暴露的全局变量,而VueComponent并没有,并且它是通过vue.extend生成的,每一次组件标签出现时,都会帮我们调用vue.extend,每次调用都会返回一个新的VueComponent
  • 也就是说多个vc实例对象对应多个VueComponent,每个VueComponent中的数据都不一样

设计思想三

在源码中找到VueComponent,改成sub.prototype.x = {a:1,b:2}

不可以这样子,如果这样还不如改成window.x

VueComponent是构造函数,也就是类,vc是类的不同实例, 类的原型上加的东西,实例vc可以用__ proto__访问

设计思想四(部份正确的)

由于VueComponent.prototype.__prp__ = Vue.prototype

因此Vue上面的方法,vc实例对象都可以访问

因此vc vm 都可以访问到Vue.prototype,因此在Vue.prototype添加方法x

import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false
Vue.prototype.x = vc/vm ?

new Vue({
  render: h => h(App),
}).$mount('#app')
  • 为什么不等于Vue.prototype??

1)首先选择vc

可以被所有组件访问到x,并且可以访问到Vue.prototype中的$on $emit $off这些方法

但是这样子写太麻烦了

因为这个x只想把它当成傀儡使用,不需要去传入具体的数据监测、数据代理等等数据

这个x的添加,必须是在new 之后,传送具体的对象之前,因此选用生命周期beforeCreate函数比较合适

import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false
const demo = Vue.extends({})
const d = new demo
Vue.prototype.x = d

new Vue({
  render: h => h(App),
}).$mount('#app')

2)选择vm

import Vue from 'vue'
import App from './App.vue'

// Vue.prototype.x = vm  这个时候vm还没有定义,会出现报错

const vm = new Vue({
  render: h => h(App),
}).$mount('#app')

// Vue.prototype.x = vm  这个时候App组件以及其他组件都已经执行完毕了,此时找不到$on $emit这些方法
  • 不应该找不到x吗?

3)选择vm (正确方法)

import Vue from 'vue'
import App from './App.vue'

new Vue({
  render: h => h(App),
  beforeCreate() {
  	Vue.prototype.$bus = this // 安装全局事件总线
  }
}).$mount('#app')

1.2 事件总线

当事件总线安装上以后

某个组件使用该事件总线,往上面绑定了事件以后,用完之后最好给它销毁掉

因为如果该组件被销毁了,由于事件总线还在,该事件就也还在,还占用着该名称,如果其他组件想要新建该名字的事件,无法新建

但是为什么自定义事件无需销毁,因为自定义事件是给该组件实例对象绑定的,该组件被销毁了,也就是该组件实例对象被销毁了,自定义事件自然不见了

基本使用和ref的使用方法类似

1.3 总结

  1. 一种组件间通信的方式,适用于任意组件间通信

  2. 安装全局事件总线:

    new Vue({
    	......
    	beforeCreate() {
    		Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm
    	},
        ......
    }) 
    
  3. 使用事件总线:

    1. 接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身。

      methods(){
        demo(data){......}
      }
      ......
      mounted() {
        this.$bus.$on('xxxx',this.demo)
      }
      
    2. 提供数据:this.$bus.$emit('xxxx',数据)

  4. 最好在beforeDestroy钩子中,用$off去解绑当前组件所用到的事件。

父亲给儿子 —— props

儿子给父亲 —— props、自定义事件

父亲给孙子、兄弟 —— 事件总线

2、消息订阅与发布

消息订阅与发布与消息总线是相反的

发布才是应用,订阅的时候,订阅回调是在本身的

订阅消息:消息名称(挂载函数)
发布消息:消息(点击事件)

订阅报纸:地址
发布报纸:报纸

感觉像是事件总线的封装,更推荐用消息总线 ==》 还是要看看,面试可能会问

  1. 一种组件间通信的方式,适用于任意组件间通信

  2. 使用步骤:

    1. 安装pubsub库:npm i pubsub-js

    2. 在需要订阅与发布的组件中引入: import pubsub from 'pubsub-js'

    3. 接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身。

      methods(){
        demo(data){......}
      }
      ......
      mounted() {
        this.pid = pubsub.subscribe('xxx',this.demo) //订阅消息
      }
      
    4. 提供数据:pubsub.publish('xxx',数据)

    5. 最好在beforeDestroy钩子中,用PubSub.unsubscribe(pid)取消订阅。 ==> 销毁的时候它接收的是id,并不是事件名称,因此需要去this.pid去接收

      1.一般在订阅中使用该钩子去取消订阅

      mounted() {
          // console.log('School',this)
          this.pubId = pubsub.subscribe('hello',(msgName,data)=>{
              console.log(this)
              // console.log('有人发布了hello消息,hello消息的回调执行了',msgName,data)
          })
      },
      beforeDestroy() {
          // this.$bus.$off('hello')
          pubsub.unsubscribe(this.pubId)
      },
      
      • 有人发布该消息之后,就会执行这个回调函数,该回调函数可以接受两个参数,第一个参数是消息名称,第二个参数是要传的参数
posted @ 2022-02-13 13:26  李小晚  阅读(193)  评论(0)    收藏  举报