发布订阅者模式与Vue兄弟组件通信

发布-订阅者模式

  由订阅者把订阅事件注册到调度中心,发布者将触发的事件发布到调度中心,再由事件中心统一调度订阅者注册到调度中心的方法

class PubSub {
    constructor() {
        this.subscribers = {}
    }
    subscribe(type, fn) {
        this.subscribers[type] = this.subscribers[type] || [];
        this.subscribers[type].push(fn);
    }
    unsubscribe(type, fn) {
        let listeners = this.subscribers[type];
        if(!listeners || !listeners.length) return ;
        this.subscribers[type] = listeners.filter(v => v !== fn);
    }
    publish(type, ...args) {
        let listeners = this.subscribers[type];
        if(!listeners || !listeners.length) return ;
        listeners.forEach(fn=> fn(...args));
    }
}

let broker = new PubSub();
broker.subscribe('add', val=> console.log(val));
broker.publish('add', 1);



Vue 兄弟组件的通信

  在Vue.js 要点与事项手记篇的结尾我们提到父子组件通信的实现方法,如果要实现兄弟组件的通信,一个比较好的办法就是由其中一个兄弟组件向父组件抛出事件,父组件收到后向另一个兄弟组件的属性传递这个数据,即:兄弟A—> 父 —> 兄弟B。

<div id="demo">
    <my-sibling-a @greet-b="dispatch"></my-sibling-a>
    <!--父组件监听子组件发出的消息-->

    <hr/>

    <my-sibling-b :msg="buffer"></my-sibling-b>
    <!--父组件将变量绑定到子组件属性-->
</div>

<script>

    const siblingA = {
        methods: {
            send: function() {
                this.$emit("greet-b",
                    "Hello, this is A");    //向父组件发送消息
            }
        },
        template: `
            <div>
                <p> component A </p>
                <button @click="send"> send message to B </button>
            </div>
        `
    };

    const siblingB = {
        props: ["msg"],
        template: `
            <div>
                <p> component B </p>
                <span>received message: {{ msg }}</span>
            </div>
        `
    };

    new Vue({
        el: "#demo",
        data: {
            buffer: null
        },
        methods: {
            dispatch: function(msg) {    //将收到的消息数据绑定到变量
                this.buffer = msg;
            }
        },
        components: {
            "my-sibling-a": siblingA,
            "my-sibling-b": siblingB,
        }
    });

</script>


 另一种比较简单方便的办法是使用Vuex来传递数据,但为了更加轻便的完成兄弟组件的通信,可以使用发布订阅者模式来完成:

  建立一根全局的消息总线作为上图中事件的调度中心,发布者向这根总线上发布消息,订阅者则监听这根总线。而通过这种方法其实可以完成任意组件的通信。

<div id="demo">
    <my-sibling-a></my-sibling-a>

    <hr/>

    <my-sibling-b></my-sibling-b>
</div>

<script>

    var bus = {
        subscribers: {}
    };    // 调度中心
    bus.subscribe = function(type, fn) {
        this.subscribers[type] = this.subscribers[type] || [];
        this.subscribers[type].push(fn);
    };
    bus.publish = function (type, ...args) {
        let listeners = this.subscribers[type];
        if (!listeners || !listeners.length) return ;
        listeners.forEach(fn => fn(...args));
    };    

    const siblingA = {
        methods: {
            send: function() {
                this.notify("Hello, this is A");    // 发布者通知调度中心
            },
            notify: function(...args) {
                bus.publish("greetB", ...args);
            }
        },
        template: `
            <div>
                <p> component A </p>
                <button @click="send"> send message to B </button>
            </div>
        `
    };
    const siblingB = {
        data: function() {
            return {
                msg: ""
            }
        },
        created: function() {
            var _this = this;
            bus.subscribe("greetB", (msg)=> {
                _this.msg = msg;
            });        // 订阅者在调度中心注册回调
        },
        template: `
            <div>
                <p> component B </p>
                <span>received message: {{ msg }}</span>
            </div>
        `
    };

    new Vue({
        el: "#demo",
        components: {
            "my-sibling-a": siblingA,
            "my-sibling-b": siblingB
        }
    });

</script>


  同样地,通过vm.$emitvm.$on,发送和监听事件消息也可以完成上面的作用效果。

<div id="demo">
    <my-sibling-a></my-sibling-a>

    <hr/>

    <my-sibling-b></my-sibling-b>
</div>

<script>

    var bus = new Vue();    // 调度中心

    const siblingA = {
        methods: {
            send: function() {
                this.notify("Hello, this is A");    // 发布者通知调度中心
            },
            notify: function(...args) {
                bus.$emit("greetB", ...args);
            }
        },
        template: `
            <div>
                <p> component A </p>
                <button @click="send"> send message to B </button>
            </div>
        `
    };
    const siblingB = {
        data: function() {
            return {
                msg: ""
            }
        },
        created: function() {
            var _this = this;
            bus.$on("greetB", (msg)=> {
                _this.msg = msg;
            });        // 订阅者在调度中心注册回调
        },
        template: `
            <div>
                <p> component B </p>
                <span>received message: {{ msg }}</span>
            </div>
        `
    };

    new Vue({
        el: "#demo",
        components: {
            "my-sibling-a": siblingA,
            "my-sibling-b": siblingB
        }
    });

</script>
-------------本文结束-------------