携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第3天,点击查看活动详情
发布订阅模式
- 发布订阅模式
-
- 发布者(发布信息)
- 订阅者(接收信息)
- 消息中心(传递信息)
首先,我们要有一个消息中心,去发布我们的消息,假设要执行事件a,我们就在要执行的时候由发布者通过消息中心去触发事件a,订阅者向消息中心订阅事件a,当订阅者收到a事件的信号时, 就去执行指定的操作,这就是发布订阅模式(publish-subscribe pattern)
个人理解:
- 订阅者要先去订阅
- 在消息中心 存储事件的键值对中存入对应的处理函数
- 当发布者 发布消息后,便去挨个执行该事件对应的处理函数
Vue的自定义事件、兄弟组件通过EventBus传值 用的就是发布订阅模式
兄弟组件传值:
//main.js
//事件中心
Vue.property.$EventBus = new Vue()
//componentA.vue
//发布者
methods:{
publishEvent(){
//发布消息 两个参数,事件名 传递的参数
this.$EventBus.$emit('add-todo',{text: this.newTodoText})
}
}
//componentB.vue
//订阅者
created() {
//订阅消息 两个参数, 事件名 处理函数(发布者传入的参数会作为事件处理函数的实参)
//最好传入具名函数,便于销毁时解绑事件
this.$EventBus.$on('add-todo', par => {
//一些操作
})
}
发布订阅模式
简单实现:
<script>
//定义事件触发器
class EventEmitter {
constructor () {
//subs是一个对象,用来存储事件名及其对应的处理函数
// {'click': [fn1, fn2], 'change': [fn]}
this.subs = Object.create(null) //设置为null表示Object.create创建的对象没有原型属性
}
//注册事件
$on (eventType, handler) { //事件类型 事件处理函数
//首先判断subs中是否存储该 事件类型
this.subs[eventType] = this.subs[eventType] || []
this.subs[eventType].push(handler)
}
//触发事件
$emit (eventType) {
if(this.subs[eventType]) {
this.subs[eventType].forEach( handler => {
handler()
})
}
}
//只允许执行一次的事件
$once(eventType, handler){
this.subs[eventType] = this.subs[eventType] || []
let onceFn = () => {
handler()
this.$off(eventType, onceFn)
}
this.$on(eventType, onceFn)
}
//解绑事件
$off(eventType, handler){
if(!this.subs.hasOwnProperty(eventType)){
throw new Error('事件未注册')
}else if(typeof handler !== 'function'){
throw new Error('请传入一个函数')
}else{
this.subs[eventType].splice(this.subs[eventType].findIndex(item => item == handler),1)
}
}
}
let bus = new EventEmitter()
bus.$on('click', () => {
console.log('click1')
})
bus.$on('click', () => {
console.log('click2')
})
bus.$emit('click')
</script>
观察者模式
- 观察者(订阅者) -- Watcher
-
- Update(): 当事件发生,具体要做的事情
- 每个观察者都存在一个update方法,当事件发生时,会调用观察者的update方法
- Vue响应式中,数据变化会调用 观察者的update方法, 用来更新视图
- 订阅者的update方法是由发布者调用的
- 目标(发布者) -- Dep
-
- subs数组: 存储所有的观察者,所有依赖该事件的观察者都会添加到数组中
- addSub(): 添加观察者
- Notify(): 当事件发生, 调用所有观察者的 update()方法
简单实现:
<script>
//发布者 - 目标
class Dep {
constructor () {
//记录所有订阅者(观察者)
this.subs = []
}
// 添加订阅者
addSub(sub) {
if(sub && sub.update){ //判断sub是否为订阅者 - 即sub存在且有update方法
this.subs.push(sub)
}
}
// 发布通知
notify() {
this.subs.forEach(sub => {
sub.update()
})
}
}
// 订阅者 - 观察者
class Watcher {
update() {
console.log('update')
}
}
//测试
let dep = new Dep()
let watcher = new Watcher()
dep.addSub(watcher)
dep.notify()
</script>
发布订阅模式与观察者模式区别
- 观察者模式
-
- 没有事件中心,只有发布者 和 订阅者, 且发布者需要知道订阅者的存在,所以发布者和订阅者之间存在依赖关系
- 由具体目标调度,比如当事件触发,Dep就回去调用观察者的update方法
- 发布订阅模式
-
- 发布者和订阅者之间不需要知道对方的存在, 发布者与订阅者之间不存在依赖关系
- 由统一的事件中心调度