Skip to content

Vue 3 實戰:掌握 Emit 事件機制實現子傳父元件通訊

Vue 3 Emit:defineEmits 與 $emit

在 Vue 的單向數據流架構中,父元件透過 props 向下傳遞資料,而子元件若要與父元件溝通,則必須透過 emit(事件)。這是一種 由下而上 的通知機制,讓子元件能在特定行為發生時,告知父層進行相應處理。

快速觸發事件(Template 內直接使用)

如果您只需要在按鈕點擊時進行簡單的通知,可以在元件的模板中直接使用內建 $emit 方法向上(父)層通知。

下(子)層發出通知

html
<input type="button" @click="$emit('someEvent')" value="通知上(父)層">

上(父)層監聽事件

父元件可以像 監聽原生 DOM 事件 一樣,使用 @v-on)來接收子元件的訊號:

html
<Counter @some-event="counter++" />
<Counter @some-event="() => counter++" />
<Counter @some-event="updateCounter" />
js
function updateCounter() {
  counter.value++;
}

小提醒

若有需要向父層傳遞特殊資料,可用參數的方式傳遞。如: $emit('someEvent', 100);

專業提醒

雖然在模板中可以寫成 someEvent,但在 Vue 慣例中,事件名稱建議在 HTML 監聽時使用 Kebab-case(如 @some-event),而在 JavaScript 中發送時使用 CamelCase

正式宣告觸發事件(使用 defineEmits

在更複雜的邏輯中,我們通常會在 <script setup> 內使用 defineEmits 來明確宣告該元件會發出的事件。這不僅讓程式碼更具閱讀性,也能讓 IDE 提供更好的補全建議。

js
// 宣告該元件擁有的自定義事件
const emit = defineEmits(['someEvent']);

function clickHandler() {
  // 執行一些邏輯處理後,再發出事件
  console.log('準備通知父層...');
  emit('someEvent');
}

進階應用:事件攜帶參數(傳遞資料)

有時候我們不只是要發出 通知 ,還需要把子元件內部的資料 傳給 父層。這時可以將資料作為 $emitemit() 的第二個參數傳出。

子元件傳遞數值

js
// 在 script setup 中傳遞
emit('updateCount', 100);

// 或在 template 中傳遞
// <button @click="$emit('updateCount', 100)">傳送 100</button>

父元件接收參數

父元件的方法會自動接收到這個傳出的值:

js
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 或狀態管理工具。

延伸閱讀