เปิดหัวข้อมาก็หาเรื่องซะแล้ว แต่หัวข้อเต็มๆของมันก็คือ Non Parent-Child Communication หมายถึง component 2 ตัวคุยกันโดยทั้ง 2 component นี้ไม่ได้เป็น parent และ child กันเลยโดยคุยผ่าน Event Bus ซึ่ง Document ที่เขียนเรื่องนี้ก็แอบอยู่ในซอกหลืบซอกใจของ Document และเขียนสั้นกระชับซะเหลือเกิน ทำให้คนเข้าใจยากแบบผมต้องหา และ กว่าจะหาเจอก็ไล่ไปยันบทความของรัสเซียกันเลยทีเดียวฮ่าๆ
ขอเกริ่นก่อนว่าทำไมถึงต้องมี Event Bus ลองคิดดูว่าถ้าเกิดมี Component ที่อยู่คนละ component แยกกันแต่อยากให้กดปุ่มที่ component A และ ไป trigger ตัว Component B ให้ทำงานยกตัวอย่าง Layout แบบนี้
จากรูปจะแยกออกเป็น 4 component ถ้าเกิดใน Header มีปุ่มอยู่และเมื่อคลิกจะทำการสั่งให้ Footer ไป call API ตัวนึงเพื่อแสดงผลข้อมูลเราจะทำอย่างไร
โดยปกติแล้วเราจะทำการใช้ Actions ของ Vuex เพื่อไปเรียก API และ ให้ Footer อ่านข้อมูลจาก Vuex มาแสดงผลแต่ใน Application ขนาดเล็ก หรือ ฟังก์ชั่นไม่ได้ยุ่งยากก็ไม่ต้องลำบากเขียน Vuex ให้วุ่นวายเราสามารถสั่งให้ Footer เรียก API ด้วยตัวเองผ่าน Event Bus
ตัว Event Bus นั้นผมมองว่ามันเป็น Base on $emit วิธีการใช้งานมันเหมือนการ $emit ค่าจาก Component ลูก ไป parent มากมาดูวิธีการทำกันดีกว่า จาก Document ของ Vue.js เค้าเขียนมาแค่นี้ !!! แต่ก็ยังพอเข้าใจได้แต่มันยังไม่ชัด
var bus = new Vue()
// in component A's method
bus.$emit('id-selected', 1)
// in component B's created hook
bus.$on('id-selected', function (id) {
// ...
})
ให้เราทำการสร้าง Vue Instant ขึ้นมาและนำตัวแปรมา $emit(‘ชื่อ Event’, data)
ใน component B เราเอา Instant ตัวเดิมมา $on(‘ชื่อ Event’, function (data) {}) เพื่อรับ event และ data ที่ถูกส่งผ่านตัว Vue Instant มา
สำหรับใครที่เขียน ES2015 ขึ้นไปสามารถใช้การ import มาช่วยได้แบบนี้
แต่สำหรับโปรเจคใหญ่ๆแล้วการมา import Event Hub ทุก component ที่จะใช้งานก็ดูจะยุ่งยากเกินไปเราสามารถยัดตัว Vue Instant ใส่ Vue.js prototype เลยเพื่อที่จะใช้งานผ่าน this ของแต่ละ Component
คำเตือน
การใช้งาน Event Bus นี้ใช้สำหรับ Component ที่ไม่ได้เป็น แม่ และ ลูก(non parent/Child) กัน หรือ component ที่ซ้อนกันเป็นชั้นๆอยู่ควรใช้การ $emit แบบธรรมดา
ใครไม่อ่านจะเสียใจ
สำหรับการ listener event นั้นถ้า component นั้นถูก ลบ หรือ เลิกใช้งานเราควรยกเลิกด้วย off() ด้วยเพราะถ้าเราไม่ยกเลิกและมีการ Set ค่าใน data หรือ $emit event ต่างๆจะเกิด Error แบบเจ็บปวดแน่นอนเพราะผมเจอมาแล้ว 555+ และ วิธีการแก้ก็ง่ายแสนง่ายแบบนี้
// life cycle Hook
beforeDestroy() {
this.$eventHub.$off('updateData') // ถ้าใช้ Vue.prototype
EventHub.$off('updateData') // ถ้าใช้ import
}
สำหรับการเลือกใช้ Vue.prototype หรือ import ไฟล์ นั้นก็ลองดูที่ขนาดโปรเจคว่าเราจำเป็นต้องใช้งานบ่อยมั้ยถ้าใช้หลายจุดหลายไฟล์ก็สามารถ Set Prototype ได้เลย