一、简单的发布订阅者模式
class Observer {
constructor() {
this.messages = []//消息队列
}
/**
* 订阅消息
* @param {类型} type
* @param {方法} fn
*/
$on(type, fn) {
if (!type) return
!this.messages[type] && (this.messages[type] = [])
this.messages[type].push(fn)
}
/**
* 订阅一次性的消息
* @param {类型} type
* @param {方法} fn
*/
$once(type, fn) {
const self = this
//内部执行方法
function _on() {
fn.apply(this, arguments) // 执行的是fn
self.$off(type, _on) // 注意,卸载的是on函数(利用了闭包)
}
this.$on(type, _on)
}
/**
* 取消订阅,如果不传fn则清除该类型下的所有方法
* @param {类型} type
* @param {要取消订阅的方法} fn
*/
$off(type, fn) {
if (!type) return
if (!fn) return this.messages[type] = undefined
this.messages[type] = this.messages[type].filter(_fn => _fn != fn)
}
/**
* 发布消息
* @param {类型} type
* @param {参数(有时候消息队列里面的方法需要传递实参进去)} param
*/
$emit(type, param) {
if (!this.messages[type]) return
this.messages[type].forEach(_fn => {
_fn(param)
});
}
}
export default Observer
二、举例一个发布-订阅者模式的生产环境用途
假设一个项目中,在vuex中的一个websocket.js模块和有一个observer.js两个vuex模块;
其中websocket.js大概维护了websocket的初始化、发送消息、接收消息的方法;
然后在A页面调用,假设收到消息后,怎么在vuex中通知A页面呢?
那么就要用到发布-订阅者模式
了,在A页面只负责ws发送消息的触发(但是需要传递主题type、回调函数fn),然后websocket.js的消息发送模块订阅主题,然后在websocket.js的接收ws消息的方法中发布主题,从而达到修改A页面数据的功能
observer.js
import Observer from '@/utils/Observer'
const getDefaultState = () => {
return {
observer: new Observer()
}
}
const state = getDefaultState()
const mutations = {
RESET_STATE: (state) => {
Object.assign(state, getDefaultState())
},
//订阅消息
OBSERVER_ON: function (state, data) {
const { type, fn } = data
if (type && fn) {
state.observer.$on(type, fn)
}
},
//订阅一次性消息
OBSERVER_ONCE: function (state, data) {
const { type, fn } = data
if (type && fn) {
state.observer.$once(type, fn)
}
},
//取消订阅
OBSERVER_OFf: function (state, data) {
const { type, fn } = data
type && state.observer.$off(type, fn)
},
//发布消息
OBSERVER_EMIT: function (state, data) {
const { type, } = data
type && state.observer.$emit(type)
},
}
export default {
namespaced: true,
state,
mutations,
}
A页面
methods: {
start(){
this.$store.commit("webSocket/SEND_MESSAGE", {
type: EM.START,//使用枚举,不适用字面量
fn: this.handleA
});
},
handleA(param) {
console.log("🚀 ~ file: electronicSignature.vue:556 ~ handleA ~ param:", param)
this.test=param.msg
}
}
websocket.js
const mutations = {
//初始化方法
INIT_WEBSOCKET:function(){
...
},
//发送消息方法
SEND_MESSAGE:function(state,data){
...
//发送的时候订阅主题
this.commit("observer/OBSERVER_ON", data)
...
},
//初接收消息方法
ON_MESSAGE:function(state,data){
...
//接收的时候的时候发布主题,data里面已经和后端约定好了会有type字段的
this.commit("observer/OBSERVER_EMIT", data)
...
},
}