文章

組件通信:自定義事件

在 Vue 3 中,組件之間的通信可以通過自定義事件來實現,這是讓子組件與父組件交互的核心方式之一。自定義事件允許子組件向父組件發送通知,並觸發父組件的相應邏輯,這種模式在父組件傳遞數據給子組件後,需要子組件進行回饋的場景中非常有用。

1. 什麼是自定義事件?

自定義事件是 Vue 中的一種通信機制,子組件可以使用 $emit() 來觸發事件,父組件可以通過監聽這些事件來響應子組件的行為。

使用場景

  • 當父組件需要知道子組件內發生的某些行為時(如按鈕點擊、表單提交等)。
  • 子組件需要向父組件傳遞信息或結果。
  • 父組件希望在子組件觸發某些操作後執行相應的邏輯。

2. 如何使用自定義事件?

自定義事件的使用分為兩部分:子組件觸發事件,以及 父組件監聽事件

1. 子組件觸發事件 ($emit)

子組件使用 $emit() 方法來觸發自定義事件。這個方法的第一個參數是事件名稱,第二個參數是可選的事件傳遞數據。

範例:

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
<div id="app">
  <child-component @custom-event="handleCustomEvent"></child-component>
</div>

<script>
const app = Vue.createApp({
  methods: {
    handleCustomEvent(payload) {
      console.log('Custom event received:', payload);
    }
  },
  components: {
    'child-component': {
      template: '<button @click="emitEvent">Click Me</button>',
      methods: {
        emitEvent() {
          this.$emit('custom-event', 'Hello from Child!');
        }
      }
    }
  }
});

app.mount('#app');
</script>

在這個範例中:

  • 子組件:當按鈕被點擊時,子組件透過 $emit('custom-event', 'Hello from Child!') 觸發一個名為 custom-event 的事件,並傳遞一個訊息作為參數。
  • 父組件:父組件使用 @custom-event="handleCustomEvent" 來監聽這個事件,當事件被觸發時,handleCustomEvent 方法會被執行,並接收到來自子組件的訊息。

2. 父組件監聽事件

父組件可以使用 Vue 的事件綁定語法 @event-name="method" 來監聽子組件觸發的自定義事件。在事件被觸發時,綁定的方法會被調用,並且可以接收子組件傳遞過來的數據。

範例中的 @custom-event="handleCustomEvent" 就是父組件監聽事件的語法。

3. 事件傳遞數據

$emit() 觸發事件時,可以傳遞任意數據給父組件。父組件可以在響應的處理方法中接收到這些數據。

範例:

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
<div id="app">
  <child-component @submit="handleSubmit"></child-component>
</div>

<script>
const app = Vue.createApp({
  methods: {
    handleSubmit(formData) {
      console.log('Form data received:', formData);
    }
  },
  components: {
    'child-component': {
      template: `
        <form @submit.prevent="submitForm">
          <input v-model="name" placeholder="Enter your name">
          <button type="submit">Submit</button>
        </form>
      `,
      data() {
        return {
          name: ''
        };
      },
      methods: {
        submitForm() {
          this.$emit('submit', this.name); // 傳遞數據給父組件
        }
      }
    }
  }
});

app.mount('#app');
</script>

在這個範例中,子組件有一個表單,當表單提交時,子組件會透過 $emit('submit', this.name) 將表單數據(使用者的姓名)傳遞給父組件。父組件會監聽 submit 事件,並通過 handleSubmit 方法處理這些數據。


4. 帶參數的事件監聽器

有時父組件可能需要在事件觸發時傳入自己的參數,這時可以使用箭頭函數來處理。

範例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<div id="app">
  <child-component @custom-event="handleCustomEvent('Parent data')"></child-component>
</div>

<script>
const app = Vue.createApp({
  methods: {
    handleCustomEvent(parentData) {
      return (childData) => {
        console.log('Parent data:', parentData);
        console.log('Child data:', childData);
      };
    }
  },
  components: {
    'child-component': {
      template: '<button @click="$emit(\'custom-event\', \'Child data\')">Click Me</button>'
    }
  }
});

app.mount('#app');
</script>

在這裡,當子組件觸發 custom-event 時,父組件既可以使用自己的參數,也可以接收子組件傳遞過來的數據。這樣的方式非常適合複雜場景。


5. 事件名的命名規範

在 Vue 中,事件名稱應遵循一個約定:事件名稱使用 kebab-case(短橫線命名)。例如,custom-event 而非 customEvent。這是因為在 HTML 中,屬性名是不區分大小寫的。

範例:

1
2
3
<div id="app">
  <child-component @custom-event="handleCustomEvent"></child-component>
</div>

在 JavaScript 內部,事件名不區分大小寫,但在 HTML 中會轉換為小寫,所以使用 kebab-case 是更一致的寫法。


6. 原生事件 vs 自定義事件

  • 原生事件(如 clickinput)是瀏覽器提供的 HTML 元素的事件,可以直接使用。
  • 自定義事件 是 Vue 組件中用來觸發和監聽的事件,這些事件並不依賴於瀏覽器的事件系統,而是 Vue 自己的機制。

如果需要將事件綁定到子組件的根 HTML 元素,而非自定義的 Vue 組件事件,可以使用 .native 修飾符來監聽子組件的原生事件:

1
<child-component @click.native="handleClick"></child-component>

這樣,click 事件會直接綁定到子組件的根元素上,而不是 Vue 自定義的 click 事件。


7. v-model 與自定義事件

Vue 的 v-model 綁定實際上是基於事件通信的。在 Vue 3 中,子組件需要透過 $emit('update:modelValue', value) 來觸發 v-model 的更新。

範例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<div id="app">
  <child-component v-model="parentValue"></child-component>
</div>

<script>
const app = Vue.createApp({
  data() {
    return {
      parentValue: 'Initial Value'
    };
  },
  components: {
    'child-component': {
      props: ['modelValue'],
      template: `<input :value="modelValue" @input="$emit('update:modelValue', $event.target.value)">`
    }
  }
});

app.mount('#app');
</script>

在這個例子中,v-model 在父組件中綁定了 parentValue,子組件透過 update:modelValue 事件來告訴父組件更新其數據。


總結

  • 子組件可以透過 $emit() 來觸發自定義事件,父組件通過 @event 語法來監聽這些事件。
  • 子組件可以向父組件傳遞事件參數,父組件可以根據參數執行相應邏輯。
  • 自定義事件是 Vue 中常見的組件通信方式,特別是父子組件之間的數據回饋。
  • Vue 3 還提供了進階的通信方式,例如 v-model 綁定以及帶參數的事件監聽。

這些特性讓 Vue 的自定義事件系統變得靈活且高效,使得組件間的交互更加流暢。

本文章以 CC BY 4.0 授權