js中的发布订阅
1.什么是发布订阅
比如:你喜欢一部动漫,但是他的更新时间是不确定的,那么你就可以订阅这个动漫的更新时间,每当动漫更新时会发布一条消息,这个时候因为你订阅这个动漫的更新时间,你就会立马收到这条消息,然后第一时间去看动漫了。例如:vue中的事件总线,$emit
和$on
, $on
用来订阅事件,$emit
用来发布订阅的事件,这样子每当$emit
发布订阅的事件时,就会执行$on
之前订阅的事件。
2.发布订阅的定义
发布-订阅模式其实是一种对象间一对多的依赖关系,当一个对象的状态发送改变时,所有依赖于它的对象都将得到状态改变的通知。
订阅者(Subscriber)把自己想订阅的事件注册(Subscribe)到调度中心(Event Channel),当发布者(Publisher)发布该事件(Publish Event)到调度中心,也就是该事件触发时,由调度中心统一调度(Fire Event)订阅者注册到调度中心的处理代码。 例如上面的例子,动漫就是一个发布者,你就是一个订阅者,订阅更新时间就是把事件注册到调度中心,当动漫更新后,动漫就是发布这个更新时间到调度中心,订阅更新时间这个事件就会被触发。
3发布订阅的实现
- list是一个调度中心(缓存所有的订阅了的函数列表)
- on方法用来订阅事件到list中去,通过key来缓存不同的事件
- emit方法用来发布事件,通过arguments拿到发布事件时传递过来的key并拿到list中的函数,在传递参数去执行函数
- remove方法用来移除list中订阅的函数
- once方法只执行一次发布订阅后就移除订阅的函数
let event = {
list: {},
// 订阅事件
on (key ,fn) {
// 如果没有这个key 就给他赋值为一个数组
if (!this.list[key]) {
this.list[key] = []
}
// 缓存列表里面加函数
this.list[key].push(fn)
},
// 发布事件
emit () {
// 通过arguments取到第一个传递的参数key
let key = [].shift.call(arguments) // arguments是类数组没有shift方法,用这种call的方式传递参数给前面的数组
let fns = this.list[key]
fns.forEach(fn => {
//arguments shift方法会改变arguments的长度,剩余的值就是函数所需要的参数
fn.apply(this, arguments)
})
},
// 移除事件
remove (key ,fn) {
let fns = this.list[key]
// 如果缓存列表没有函数直接返回
if (!fns) return false
if (!fn) {
fns && (fns.length = 0) // .length = 0 清空数组
} else {
fns.forEach((cb, i) => {
if (cb === fn) {
fns.splice(i, 1)
}
})
}
},
// once
once (key, fn) {
let on = function () {
fn.apply(this, arguments)
this.remove(key, on)
}
this.on(key, on)
}
}
// console.log(event)
let fn = function (data, data1) {
console.log(data, data1)
}
event.on('get', fn)
event.once('post', fn)
event.emit('get', 1, 'a') // 1, a
event.emit('get', 2, 'b') // 2, b
event.emit('post', 3, 'c') // 3, c
event.emit('post', 4, 'd') // 不执行
event.remove('get', fn)
event.emit('get', 5, 'e') // 不执行
4.如何使用
在微信小程序开发中,你需要兄弟组件(a, b)之间进行联系,如果业务需求为:点击a组件的中的一个按钮,b组件中触发一个事件,这个时候就可以用到发布订阅模式,先在b组件中的一个生命周期(onLoad, onReady等)里订阅一个事件为tapB,然后在a组件中点击按钮时就发布这个tapB事件,这个时候b组件中订阅的事件就会执行了。