发布订阅模式
发布订阅模式和观察者模式是两种设计模式,在 Vue 中有各自的应用场景。两种模式的本质是相同的,但是还是有区别的,经常被混为一谈。
发布/订阅模式
- 订阅者
- 发布者
- 信号中心
我们假定,存在一个"信号中心",某个任务执行完毕,就向信号中心"发布"(publish)一个信号,其他任务可以向信号中心"订阅"(subscribe)这个信号,从开知道什么时候自己可以执行。这就叫做**“发布/订阅模式”(publish-subscribe pattern)**\
从自定义事件中看订阅/发布模式
let vm = new Vue()
// 订阅1
vm.$on('dataChange', () => {
console.log('dataChange')
})
// 订阅2
vm.$on('dataChange', ()=> {
console.log('dataChange1')
})
// 发布
vm.$emit('dataChange')
在上述的 vue 的自定义事件中,订阅1 和 订阅2 都订阅了同一个信号 “dataChange” ,当通过 vm.$emit(‘dataChange’) 发布这个信号的时候,订阅者们就会接收到信号开始执行各自的操作。这不过这里面的所有的订阅者和发布者都是一个角色 vm 。
从父子组件通信中看订阅/发布模式
// eventBus.js
// 事件中心
let eventHub = new Vue()
// ComponentA.vue
// 发布者
addTodo: function () {
// 发布消息(事件)
eventHub.$emit('add-todo', { text: this.newTodoText })
this.newTodoText = ''
}
// ComponentB.vue
// 订阅者
created: function () {
// 订阅消息(事件)
eventHub.$on('add-todo', this.addTodo)
}
模拟 Vue 自定义事件的实现(发布/订阅模式)
<script>
// 事件触发器
class EventEmitter {
constructor () {
// { 'click': [fn1, fn2], 'change': [fn] }
this.subs = Object.create(null) // 使用 Object.create() 的方法创建一个对象可以传递一个参数,这个参数的作用是去设置创建的对象的原型,设置为 null 声明这个对象没有原型属性,可以提高性能。以为这里的对象只需要存储键值对的形式,所以不需要原型。
}
// 注册事件
$on (eventType, handler) {
// 判断 this.subs[eventType] 是否存在,如果存在返回原数组,否则创建这个数组
this.subs[eventType] = this.subs[eventType] || []
this.subs[eventType].push(handler)
}
// 触发事件
$emit (eventType) {
// 这里的 $emit 只接收一个参数,是一个省略的写法,不考虑事件传参的情况
if (this.subs[eventType]) {
this.subs[eventType].forEach(handler => {
handler()
})
}
}
}
// 测试
let em = new EventEmitter()
em.$on('click', () => {
console.log('我是 click 的订阅者1')
})
em.$on('click', () => {
console.log('我是 click 的订阅者2')
})
em.$emit('click')
</script>