在vue專案中自定義事件匯流排eventHub

南半球的鯨發表於2021-01-03

由於在工作中vue的專案中使用了大量是全域性事件匯流排廣播,使得某些方法在某種情況下被重複多次呼叫,檢視了一下原因,是因為在每個單檔案元件中定義的事件接收器和事件廣播器在頁面銷燬的時候沒有登出導致的。於是在保證改動量不大的前提下,決定自定義一個全域性事件匯流排檔案。

之前的使用是在 main.js 檔案中 vue.prototype.$eventHub = vue.prototype.$eventHub || new Vue()

因此,重新定義一個檔案 EventHub.js

/**
 * Created by baidm in 2021/1/3 on 12:54
 */
class EventHub {
    constructor(vm) {
        this.vm = vm;
        this.curVm = null;
        this.events = {};
        this.eventMapUid = {};
    }

    /**
     * 註冊呼叫者例項
     * @param vm
     */
    $register(vm) {
        this.curVm = vm;
    }

    /**
     * 收集所有的事件型別
     * @param uid
     * @param type
     */
    setEventMapUid(uid, type) {
        if (!this.eventMapUid[uid]) {
            this.eventMapUid[uid] = [];
        }
        this.eventMapUid[uid].push(type);
    }

    /**
     * 收集每種型別的回撥
     * @param type
     * @param fn
     */
    $on(type, fn) {
        if (!this.events[type]) {
            this.events[type] = [];
        }
        this.events[type].push(fn);
        if (this.curVm instanceof this.vm) {
            this.setEventMapUid(this.curVm._uid, type);
        }
    }

    /**
     * 觸發每種型別的所有回撥
     * @param type
     * @param args
     */
    $emit(type, ...args) {
        if (this.events[type]) {
            this.events[type].forEach(fn => fn(...args));
        }
    }

    /**
     * 取消訂閱事件
     * @param type
     * @param fn
     */
    $off(type, fn) {
        if (fn && this.events[type]) {
            const index = this.events[type].findIndex(f => f === fn);
            if (index !== -1) {
                this.events[type].splice(index, 1);
            }
            return;
        }
        delete this.events[type];
    }

    /**
     * 取消uid訂閱的所有事件
     * @param uid
     */
    $offAll(uid) {
        const curAllTypes = this.eventMapUid[uid] || [];
        curAllTypes.forEach(type => this.$off(type));
        delete this.eventMapUid[uid];
    }
}

export default {
    install(vm, options = {}) {
        Reflect.defineProperty(vm.prototype, "$eventHub", {
            value: new EventHub(vm)
        })
    }
}

然後在 main.js 檔案中定義 mixin

import Vue from 'vue'
...

import EventHub from "./EventHub"

EventHub.install(Vue);

const mixin = {
    data() {
        return {}
    },
    created() {
        this.$eventHub.$register(this);
    },
    methods: {},
    directives: {},
    beforeDestroy() {
        this.$eventHub.$offAll(this._uid);
    }
};
Vue.mixin(mixin);

...

這樣在不改變原有程式碼中的寫法 this.$eventhub.$on() 和 this.$eventHub.$emit() 的前提下,即可在每個SFC解除安裝的時候自動下載在該元件中定義的事件廣播。

相關文章