iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 17
0
Modern Web

30天手把手的vue.js教學!系列 第 17

2020it邦鐵人賽-30天手把手的Vue.js教學 Day17 - 認識EventBus,實現子層組件間的相互溝通

tags: Vue.js ItIron2020

前言

我們在昨天介紹了vue-cli這個好東西,利用它可以超快速的打造一個完整的vue專案,同時也簡單介紹了專案內的結構,今天我們要探討的主題與之前提過的組件溝通有關係,同時也是為了之後的集中資料管理鋪路,算是一個複習+預習的過程,那我們就馬上開始吧!

我不是已經會組件間的資料傳遞了嗎? 阿不就props & emit?

snarky face

是的,你說的沒錯,在之前的todo-list組件與父層app組件的範例中,我們確實利用了props & emit實現了父子層之間的溝通,但不知道你有沒有想過萬一今天是以下的情況呢?

<template>
  <div>
      <component-a></component-a>
      <component-b></component-b>
  </div>
</template>

很不巧的這兩個組件的資料會相互影響,比方說一邊是顯示資料、另一邊則是新增/刪除資料,很明顯你無法單靠props & emit達成子層間的資料溝通,這時候就需要EventBus的概念出場了!

什麼是EventBus

不用緊張,程式語言的一大特色就是會將很單純的概念用很複雜的名詞包起來,讓我們在講話時聽起來很高端、大氣、上檔次,實際上EventBus就只是一個vue實體而已

也就是說大概是這樣的玩意兒

// EventBus.js
import Vue from "vue"
export const EventBus = new Vue()

為什麼這樣簡單的一個vue實體能協助我們進行組件間的溝通呢? 請你回想一下emit的概念

子層自訂事件傳給父層 -------> 父層監聽此事件並執行對應的handler

EventBus也是完全相同的概念,只是今天它是一個獨立的實體且作為所有組件的自訂事件監聽者,我有找到一個還不錯的概念圖

event bus

圖片來源

簡單說就是我們所有的組件共同搭乘一輛公車,那今天每個組件都跟司機說若發生A情況,那就請你做B事件,由於每個組件都在同一輛車上,處理時自然也會相互影響到!

簡易實作EventBus,前置作業

上方的說明可能讓你更加困惑了,my bad! 還是實際來做一次吧!
我們接續昨天建立的vue-cli專案,若沒有的話就直接利用vue create再建一個即可! 首先請你到src目錄下新增一個EventBus.js檔案,並寫入以下的內容

// EventBus.js
import Vue from "vue"
export const EventBus = new Vue()

接著到src/components新增以下兩隻檔案,並分別寫入指定的內容

// DisplayNumbers.vue

<template>
  <div>
    <h2>{{ numbers }}</h2>
  </div>
</template>

<script>
export default {
  data() {
    return {
      numbers: [1,2,3,5,7]
    }
  },
}
</script>

// AddNumbers.vue

<template>
  <div>
    <label for="number"></label>
    <input
      type="text"
      name="number"
      placeholder="Enter a number"
      v-model="number"
    />
    <button @click="addNumber">Add number</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      number: 0
    }
  },
  methods: {
    addNumber() {
      console.log(this.number)
    }
  }
}
</script>

最後回到App.vue,將原本預設的內容砍的乾乾淨淨,並引入兩個子元件,最終改寫為下方的程式碼。

<template>
  <div id="app">
    <DisplayNumbers />
    <AddNumbers />
  </div>
</template>

<script>
import AddNumbers from './components/AddNumbers.vue'
import DisplayNumbers from './components/DisplayNumbers.vue'

export default {
  name: 'App',
  components: {
    AddNumbers,
    DisplayNumbers
  }
}
</script>

小提醒

有人可能會好奇,我們在之前的文章說過在template使用組件時需要轉為kebab case並用-連接,不過實際上你可以在template中直接用你在下方componets屬性的名稱,只不過在解析的時候它會被視為轉為kebab case的結果,也就是說,下方兩者最終會有一樣的效果

<AddNumbers/> => <add-numbers></add-numbers>

現階段你就把它當成一種short hand即可,不需要太在意:D

完成以上的操作後,請你在終端機輸入npm run serve啟動專案,沒意外的話你會看到以下的畫面。

demo 2

至此我們前置作業就完成了!

簡易實作EventBus,實作部分

先思考一下,我們要如何利用AddNumbers組件去影響DisplayNumbers組件?

  1. 在AddNumbers組件點擊Add number時要觸發一個自訂事件
  2. 將該事件傳遞給eventbus,同時夾帶當時的數字(payload)
  3. 在DisplayNumbers組件利用eventbus監聽該自訂事件,若事件觸發則將傳來的數字推進原本的numbers陣列

這樣看起來就簡單多了,首先我們到AddNumbers.vue修改一下程式碼!

// AddNumbers.vue

import {EventBus} from '../EventBus.js' // 引入EventBus實體

export default {
  data() {
    return {
      number: 0
    }
  },
  methods: {
    addNumber() {
      EventBus.$emit('addNumber',parseInt(this.number))
    }
  }
}
</script>

我們在其中引入EventBus這個vue實體,並在點擊按鈕時註冊一個addNumber事件給它,同時將this.number作為payload傳進去,接著我們需要修改一下負責顯示的組件

// DisplayNumbers.vue

import {EventBus} from '../EventBus.js'

export default {
  data() {
    return {
      numbers: [1,2,3,5,7]
    }
  },
  methods: {
    handleAddNumber() {
      EventBus.$on('addNumber',(payload)=>{
        this.numbers.push(payload)
      })
    }
  },
  created() {
    this.handleAddNumber() // 在建立時就開始監聽
  }
}
</script>

在DisplayNumbers組件做的事情大同小異,我們一樣先引入了EventBus,同時建立一個methods監聽addNumber事件,並傳入一個callback告訴它這事件發生時要做什麼,最後則是在created hooks就開始監聽這個事件! 你自然也可以在mounted階段做!

最後請你回到頁面上,隨便輸入一個數字並按下按鈕,你會發現它順利的運作了!

demo3

結語

我們今天介紹了EventBus的概念,藉由EventBus可以實現同層組件的互相溝通,在專案規模小的時候不失為一個很棒的解決方案! 其中的概念並不複雜,有興趣的話也可以跟著教學做一次,相信會讓你更印象深刻的! 那我們就明天再見囉! 祝大家中秋佳節愉快!

此文章同步發布於個人部落格,有興趣的大大也可以來參觀一下:D

參考文章

Event Bus


上一篇
2020it邦鐵人賽-30天手把手的Vue.js教學 Day16 - 認識vue-cli,建立第一個本地的vue專案!
下一篇
2020it邦鐵人賽-30天手把手的Vue.js教學 Day18- 建立簡單的store集中管理資料吧!
系列文
30天手把手的vue.js教學!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言