# EventBus

說明

主用於 事件 傳遞,不含 的傳遞。

# 事件傳遞

利用原生事件

oldDriver.js

class OldDriver {
  constructor() {
    this.driver = document.createElement('bus');
  }

  $on(event, callback) {
    this.driver.addEventListener(event, callback, false);
  }

  $off(event, callback) {
    this.driver.removeEventListener(event, callback, false);
  }

  $emit(event, payload = {}) {
    // 觸發成功返回 true
    this.driver.dispatchEvent(new CustomEvent(event, { detail: payload }));
  }
}

export default new OldDriver();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

main.js










 




import EventBus from '@/extends/oldDriver.js';
Object.defineProperties(Vue.prototype, {
  $eventBus: {
    get: () => EventBus
  }
})
new Vue({
  mounted(){
    // 註冊事件
    this.$eventBus.$on('changeAge', console.log);
  }
  render: h => h(App)
}).$mount('#app')
1
2
3
4
5
6
7
8
9
10
11
12
13

App.vue






 




export default {
  name: 'App',
  mounted() {
    setTimeout(() => {
      // 觸發事件
      this.$eventBus.$emit('changeAge', 30);
    }, 3000);
  },
};
1
2
3
4
5
6
7
8
9

# 值的傳遞

利用 Vue 的特性

main.js



 







 




import Vue from 'vue';

Vue.prototype.$eventBus = new Vue();

new Vue({
  data: {
    list: [1, 2, 3];
  },
  mounted(){
    // 註冊事件
    this.$eventBus.$on('changeAge', () => this.list);
  }
  render: h => h(App)
}).$mount('#app')
1
2
3
4
5
6
7
8
9
10
11
12
13
14

子組件




 



export default {
  name: 'About',
  mounted() {
    console.log(this.$eventBus._events['changeAge'][0].apply());
  },
};
1
2
3
4
5
6

提醒

  • 事件的 命名 一樣的時候,回呼函式需根據 索引 對應相對事件。

    events

  • 若移除時不指定回呼函式,則 相同命名 的事件會一起被移除。

  • 若想知道綁定了什麼事件,可以查看 _events 這個物件。

    • _events 物件中,同名事件會以陣列的方式堆疊。

# 綜合應用

bus.js

export default function Bus(vue) {
  // 存放所有 component 的 method
  this.handles = {
    // event: [function, function,...]
  };
  // 存放所有 component的 uid 和 method
  this.eventUidMap = {
    // uid:{
    //   event:[function, function]
    // }
  };
  // 註冊事件
  this.$on = (event, callback, vm) => {
    if (!this.handles[event]) this.handles[event] = [];
    if (callback instanceof Function) this.handles[event].push(callback);
    if (vm instanceof vue) this.setEventUidMap(vm._uid, event, callback);
  };
  this.setEventUidMap = (uid, event, callback) => {
    if (!this.eventUidMap[uid]) this.eventUidMap[uid] = {};
    if (!this.eventUidMap[uid][event]) this.eventUidMap[uid][event] = [];
    this.eventUidMap[uid][event].push(callback);
  };
  this.$off = (event, callback) => {
    if (!this.handles[event]) return;
    // delete object property
    if (!callback) delete this.handles[event];
    else if (callback instanceof Function) {
      let len = this.handles[event].length;
      for (let i = 0; i < len; i++) {
        let cb = this.handles[event][i];
        // delete function
        if (cb === callback) this.handles[event].splice(i, 1);
      }
    }
  };
  // 刪除 component 的 uid
  this.$offByUid = (uid) => {
    let eventObj = this.eventUidMap[uid] || {};
    // 遍遞每一個 event
    Object.keys(eventObj).forEach((event) => {
      // 遍地每一個 event 的 function
      eventObj[event].forEach((cb) => {
        this.$off(event, cb);
      });
      // delete all event
      // delete eventObj[event]
    });
    delete this.eventUidMap[uid];
  };
  // 觸發事件
  this.$emit = (event, ...args) => {
    // return 的資料都丟來這
    // 如果沒 return,則回傳 undefined
    let result = [];
    if (this.handles[event]) {
      let len = this.handles[event].length;
      for (let i = 0; i < len; i++) {
        result.push(this.handles[event][i](...args));
      }
    }
    return result;
  };
  return this;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64

main.js

import Bus from '@/bus.js';

let eventBus = {
  install(vue) {
    let bus = new Bus(vue);
    // 等同是 Vue.prototype.$eventBus = ...
    // 因為要做成唯讀
    Object.defineProperties(Vue.prototype, {
      $eventBus: {
        get: () => bus,
      },
    });
    // 所有的 component,都可以有 mixin 內的功能
    Vue.mixin({
      beforeDestroy() {
        // this 是 VueComponent
        this.$eventBus.$offByUid(this._uid);
      },
    });
  },
};
// 預設會自動把 Vue 傳進去
Vue.use(eventBus);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

父組件註冊事件:






















 
 



export default {
  name: 'levelOne',
  data() {
    return {
      list: ['html', 'css', 'javascript'],
    };
  },
  components: {
    LevelTwo,
  },
  methods: {
    eventHandler(...arg) {
      console.log(arg);
      console.log(this.list);
    },
    getListHandler() {
      return { list: this.list };
    },
  },
  created() {
    // 新增事件到 event bus
    this.$eventBus.$on('getListHandler', this.getListHandler, this);
    this.$eventBus.$on('eventHandler', this.eventHandler, this);
  },
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

子組件呼叫事件和接收資料:










 
 
 
 
 
 
 
 




export default {
  name: 'levelTwo',
  data() {
    return {
      list: [],
    };
  },
  methods: {
    displayListHandler() {
      this.$eventBus.$emit('getListHandler').forEach((item) => {
        // 沒有回傳值的,會是 undefined
        if (item) {
          Object.keys(item).forEach((key) => {
            if (key === 'list') this.list = item[key];
          });
        }
      });
    },
  },
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Last Updated: 2/25/2021, 7:56:51 AM