发布订阅模式是什么

它其实是一种对象间一对多的依赖关系,当一个对象的状态发送改变时,所有依赖于它的对象都将得到状态改变的通知。(如微信公众号一样有无数个订阅者,有一个发布者,当发布者发布文章的时候,订阅者都可以收到发布的文章。)

平时使用

前端在之前使用jq的时候就有使用到发布订阅模式例如有trigger和on方法,现在vue中也经常使用到如:事件总线进行组件间的传值、子父组件间的传值、vue实现双向绑定等等。

手动实现

我们平时用的发布订阅模式需要有订阅on方法和发布emit方法,所以我们首先实现一个最简单的发布订阅。如:

class event {
    constructor() {
        // 创建一个队列存储方法
        this.eventList = [];
    }

    on(fn) {
        if (typeof fn !== 'function') return false;
        // 将传入的方法存储至队列中
        this.eventList.push(fn);
    }

    emit(...res) {
        let eventList = this.eventList;
        if (eventList.length === 0) return false;
        // 触发已经存储的方法
        eventList.forEach(fn => {
            fn(...res);
        });
    }
}
// 使用
function test(data1, data2) {
    console.log(data1);
    console.log(data2);
}
function test2() {
    console.log(2222222);
}

let e = new event();

e.on(test);
e.on(test2);
e.emit('我想我是个参数', '我想我也是一个参数');

// 我想我是个参数
// 我想我也是一个参数
// 2222222

上述代码创建了一个event类,在构造函数中定义一个存储方法的队列eventList,定义on方法进行方法绑定(也就是订阅),定义emit方法进行方法触发(也就是发布)。
在使用的时候使用on方法直接将定义好的方法全部存储至队列里面,然后使用emit方法挨个触发并传入参数即可(注意:要先订阅在发布才能收到消息),这就实现最简单的发布订阅模式,但是在使用的时候我们一般不会这样一股脑把方法全部存储至队列中,还会给需要给绑定和触发的方法添加名字,所以下面就改写上面的代码:

class event {
    constructor() {
        // 创建一个对象存储方法
        this.eventList = {};
    }

    on(name, fn) {
        if (typeof fn !== 'function') return;
        let eventList = this.eventList;
        if (!eventList[name]) eventList[name] = [];
        // 将传入的方法存储至对应的队列中
        eventList[name].push(fn);
    }

    emit(name, ...res) {
        const eventList = this.eventList[name];
        if (!eventList || eventList.length === 0) return;
        // 触发已经存储的方法
        eventList.forEach(fn => {
            fn(...res);
        });
    }
}
// 使用
function test(data1, data2) {
    console.log(data1);
    console.log(data2);
}
function test2() {
    console.log(2222222);
}

let e = new event();

e.on('test', test);
e.on('test', test2);
e.on('test2', test2);
e.emit('test2', '我想我应该是一个参数', '我想我也是一个参数');
// 2222222 
e.emit('test', '我想我应该是一个参数', '我想我也是一个参数');
// 我想我应该是一个参数
// 我想我也是一个参数
// 2222222

上述代码就是改版之后的代码,可以将方法绑定到指定name的队列,可以去触发指定name的队列的方法。但是有时候我们还想要删除指定name队列中已绑定的方法,所以我们需要在完善下代码如:

class event {
    constructor() {
        // 创建一个对象存储方法
        this.eventList = {};
    }

    on(name, fn) {
        if (typeof fn !== 'function') return;
        let eventList = this.eventList;
        if (!eventList[name]) eventList[name] = [];
        // 将传入的方法存储至对应的队列中
        eventList[name].push(fn);
    }

    emit(name, ...res) {
        const eventList = this.eventList[name];
        if (!eventList || eventList.length === 0) return;
        // 触发已经存储的方法
        eventList.forEach(fn => {
            fn(...res);
        });
    }

    remove(name, fn) {
        if (typeof fn !== 'function') return;
        let eventList = this.eventList[name];
        if (!eventList || eventList.length === 0) return;
        // 删除传入的方法
        for (var i = 0; i < eventList.length; i++) {
            if (eventList[i] === fn) {
                eventList.splice(i, 1);
                i--;
            }
        }
    }
}
// 使用
function test(data1, data2) {
    console.log(data1);
    console.log(data2);
}
function test2() {
    console.log(2222222);
}

let e = new event();

e.on('test', test);
e.on('test', test2);
e.on('test2', test2);
e.emit('test2', '我想我应该是一个参数', '我想我也是一个参数');
// 2222222 
e.emit('test', '我想我应该是一个参数', '我想我也是一个参数');
// 我想我应该是一个参数
// 我想我也是一个参数
// 2222222
e.remove('test', test);
e.emit('test', '我想我应该是一个参数', '我想我也是一个参数');
// 2222222

上述代码增加了remove删除方法,传入需要删除的队列名称和方法即可删除指定队列里面的指定方法。至此我们自己的发布订阅模式就写完了。

Logo

前往低代码交流专区

更多推荐