Vue 3 實戰:掌握 Emit 事件機制實現子傳父元件通訊
在 Vue 的單向數據流架構中,父元件透過 props 向下傳遞資料,而子元件若要與父元件溝通,則必須透過 emit(事件)。這是一種 由下而上 的通知機制,讓子元件能在特定行為發生時,告知父層進行相應處理。
快速觸發事件(Template 內直接使用)
如果您只需要在按鈕點擊時進行簡單的通知,可以在元件的模板中直接使用內建 $emit 方法向上(父)層通知。
下(子)層發出通知
<input type="button" @click="$emit('someEvent')" value="通知上(父)層">上(父)層監聽事件
父元件可以像 監聽原生 DOM 事件 一樣,使用 @(v-on)來接收子元件的訊號:
<Counter @some-event="counter++" />
<Counter @some-event="() => counter++" />
<Counter @some-event="updateCounter" />function updateCounter() {
counter.value++;
}小提醒
若有需要向父層傳遞特殊資料,可用參數的方式傳遞。如: $emit('someEvent', 100);
專業提醒
雖然在模板中可以寫成 someEvent,但在 Vue 慣例中,事件名稱建議在 HTML 監聽時使用 Kebab-case(如 @some-event),而在 JavaScript 中發送時使用 CamelCase。
正式宣告觸發事件(使用 defineEmits)
在更複雜的邏輯中,我們通常會在 <script setup> 內使用 defineEmits 來明確宣告該元件會發出的事件。這不僅讓程式碼更具閱讀性,也能讓 IDE 提供更好的補全建議。
// 宣告該元件擁有的自定義事件
const emit = defineEmits(['someEvent']);
function clickHandler() {
// 執行一些邏輯處理後,再發出事件
console.log('準備通知父層...');
emit('someEvent');
}進階應用:事件攜帶參數(傳遞資料)
有時候我們不只是要發出 通知 ,還需要把子元件內部的資料 傳給 父層。這時可以將資料作為 $emit 或 emit() 的第二個參數傳出。
子元件傳遞數值
// 在 script setup 中傳遞
emit('updateCount', 100);
// 或在 template 中傳遞
// <button @click="$emit('updateCount', 100)">傳送 100</button>父元件接收參數
父元件的方法會自動接收到這個傳出的值:
function handleUpdate(payload) {
console.log('接收到來自子層的資料:', payload); // 輸出:100
counter.value = payload;
}總結:元件溝通的核心原則
- props:往下傳,資料從父層傳給子層。
- emits:往上傳,事件從子層通知父層。
透過這套 資料下行、事件上行 的機制,我們能確保元件之間的耦合度降低,讓應用的狀態管理變得更加可預測且易於維護。
若父子之間需要持續同步同一份資料,可以進一步用 defineModel 把 props 與 emit 合併成元件的 v-model。
常見問題
$emit 與 defineEmits 有什麼差別?
$emit 可以直接在模板中呼叫,適合單純的通知;defineEmits 則是在 <script setup> 中明確宣告元件會發出哪些事件,可讀性與編輯器提示都比較好,複雜元件建議使用。
事件名稱該用 camelCase 還是 kebab-case?
在 JavaScript 中發送時用 camelCase,在模板監聽時用 kebab-case,例如:emit('updateCount') 對應 @update-count。Vue 會自動轉換。
怎麼把資料一起傳給父層?
把資料放在 emit 的第二個以後的參數,例如:emit('updateCount', 100)。父層的處理函式會依序接收到這些參數。
為什麼父層監聽不到子層發出的事件?
多半是命名沒有對上,模板監聽要用 kebab-case;也可能是事件名稱拼錯,或誤把事件寫在包裝元件的外層而沒有轉發出去。
emit 與 v-model 該怎麼選?
只是通知父層做某件事,用 emit;若父子之間需要持續雙向同步同一份資料,用 defineModel 建立元件的 v-model 會更精簡。
自訂事件會冒泡嗎?
不會。Vue 的自訂事件只會發送給直接的父元件,不像原生 DOM 事件會往上層傳遞。跨多層溝通請改用 provide / inject 或狀態管理工具。