Skip to content

发布订阅模式

概念

一种对象间一对多的依赖关系,当一个对象的状态发送改变时,所有依赖于它的对象都将得到状态改变的通知

和观察者模式的区别:发布者不直接触及到订阅者、而是由统一的第三方来完成实际的通信的操作

EventEmitter

js
class EventEmitter{
    constructor() {
        this.events = {} // 维护一个对象
    }
    /**
     * 订阅
     * @param {类型名} name 
     * @param {订阅的方法} fn 
     */
    on(name, fn) {
        if (!this.events[name]) {
            this.events[name] = []
        }
        this.events[name].push(fn)
    }
    /**
     * 只订阅一次
     * @param {类型名} name 
     * @param {订阅的方法} fn 
     */
    once(name, fn) {
        const wrapper = (...args) => {
            fn(...args)
            this.off(name, wrapper)
        }
        this.on(name, wrapper)
    }
    /**
     * 发布
     * @param {类型名} name 
     * @param  {传入的参数} args 
     */
    emit(name, ...args) {
        if (this.events[name]) {
            // 做一次浅拷贝,主要目的是为了避免通过 once 安装的监听器在移除的过程中出现顺序问题
            const events = this.events.slice()
            events[name].forEach(item => {
                item.apply(this, args)
            })
        }
    }
    /**
     * 移除类型
     * @param {类型名} name 
     */
    remove(name){
        if (this.events[name]) {
            delete this.events[name]
        }
    }
}

然后就可以愉快的使用了,创建一个 js/ts 文件,在文件中创建一个实例

js
const EventBus = new EventEmitter()
export default EventBus

订阅事件:

js
import bus from '@/event-emitter.js'
bus.on('name', fn)

发布(触发)事件:

js
import bus from '@/event-emitter.js'
bus.emit('name', params)

VUE 双向绑定

首先会有一个 observer(监听器),这个 observer 不是观察者,他是发布订阅模式里的第三方

当数据又改变会被 observer 看见,也就是触发 emit,然后 observer 就会通知底下的 dep

实现订阅者 Dep:

js
class Dep {
    constructor() {
        // 初始化订阅队列
        this.subs = []
    }
    
    // 增加订阅者
    addSub(sub) {
        this.subs.push(sub)
    }
    
    // 通知订阅者(是不是所有的代码都似曾相识?)
    notify() {
        this.subs.forEach((sub)=>{
            sub.update()
        })
    }
}

实现监听器 observer

js
// observe方法遍历并包装对象属性
function observe(target) {
    // 若target是一个对象,则遍历它
    if(target && typeof target === 'object') {
        Object.keys(target).forEach((key)=> {
            // defineReactive方法会给目标属性装上“监听器”
            defineReactive(target, key, target[key])
        })
    }
}

// 定义defineReactive方法
function defineReactive(target, key, val) {
    // 属性值也可能是object类型,这种情况下需要调用observe进行递归遍历
    observe(val)
    // 为当前属性安装监听器
    Object.defineProperty(target, key, {
         // 可枚举
        enumerable: true,
        // 不可配置
        configurable: false, 
        get: function () {
            return val;
        },
        // 监听器函数
        set: function (value) {
            val = value
            dep.notify()
        }
    });
}