iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 20
0
自我挑戰組

每天來點 Vue.js 吧系列 第 20

Vue 自定義事件 - 拋出子組件數據

Props down, Event up ✐

Prop 是數據由父組件向子組件流動的 單向數據流,也就代表子組件無法透過 Prop 更改父組件的數據(注意,盡量不要 prop 物件,由於 call by reference,子組件更動物件 prop 會影響到父組件),這時候若是想要將子組件的數據傳遞給父組件,可以透過子組件 自定義事件 並將想傳遞給父組件的數據透過 $emit 第二個參數定義。父組件對於該 自定義事件 監聽,一旦 自定義事件 發生,數據便可以自子組件中拋出給父組件。

簡單來說,一旦涉及父、子組件的數據傳遞,可以透過簡單的口訣 Props down, Event up 記憶:

  • 父組件 透過 Prop 將數據傳遞給 子組件。(下箭頭)
  • 子組件 設定 自定義事件 同時定義事件發生時將傳遞給父組件的數據,父組件透過監聽該 自定義事件 得到子組件傳出的數據。(上箭頭)

建立子組件 自定義事件

在此建立一個簡單的子組件 自定義事件,透過 instance method $emit('event-name', [...arguments]) 觸發當前 instance 組件事件。

使用 $emit 非常簡單,只需要定義自定義事件名稱,並且透過第二個參數定義隨事件拋出的子組件數據,該參數會傳給事件監聽器的 callback


補充
和 prop 不同,自定義事件名稱不會作為 javascript 變數或 property,所以不需要使用 camelCase,Vue 推薦使用 kebab-case 事件名稱。
o → my-event ; x → myEvent


一旦點擊子組件按鈕,將透過 $emit 發送 自定義事件 component-event,並且我們也定義了要隨事件拋出的數據 number

Vue.component("my-component", {
  data() {
    return {
      number: 12
    }
  },
  template: `
  <div class="child">
    <span>? 這是一個子組件</span>
    <button @click="$emit('component-event', number)">父組件 count 加 {{ number }}</button>
  </div>`
});

同一時間,父組件需要監聽該自定義事件,並且透過 $event 訪問被拋出的數據 number

這樣透過監聽自定義事件的方法,子組件的數據便可以傳遞給父組件。在需要子組件數據時,便可以使用此方法。

const vm = new Vue({
  el: ".app",
  data: {
    count: 0
  },
  template: `
    <div class="app">
      <p>父組件總計數:{{ count }}</p>
      <my-component @component-event="count+=$event" />
    </div>`
});

透過 $emit 傳出多個數據

有時候,會需要將多筆數據自子組件中拋出,這個時候使用 $event 會只拿到第二個被拋出的參數。

Vue.component("my-component", {
  data() {
    return {
      number: 12,
      text: "hello",
      author: "RURU"
    };
  },
  template: `
  <div class="child">
    <span>? 這是一個子組件</span>
    <button @click="$emit('component-event', text, author, number)">拋出值 number: 12, text: 'hello', author: 'RURU' 給父組件</button>
  </div>`
});

const vm = new Vue({
  el: ".app",
  data: {
    count: 0,
    data: null
  },
  template: `
<div class="app">
  <p>父組件接收到的子組件值:{{ data }}</p>
  <my-component @component-event="print($event)" />
</div>`,
  methods: {
    print(number, text, author) {
      console.log(number, text, author);
      this.data = { number, text, author };
    }
  }
});

我們可以看到傳出的三個參數 numbertextauthor,透過 $event 我們只能拿到第一個。

這時候可以透過 arguments 取得所有拋出的數據。^來源^

Vue.component("my-component", {
  data() {
    return {
      number: 12,
      text: "hello",
      author: "RURU"
    };
  },
  template: `
  <div class="child">
    <span>這是一個子組件</span>
    <button @click="$emit('component-event', text, author, number)">拋出值 number: 12, text: 'hello', author: 'RURU' 給父組件</button>
  </div>`
});

const vm = new Vue({
  el: ".app",
  data: {
    count: 0,
    data: null
  },
  template: `
<div class="app">
  <p>父組件接收到的子組件值:{{ data }}</p>
  <my-component @component-event="print(...arguments)" />
</div>`,
  methods: {
    print(number, text, author) {
      console.log(number, text, author);
      this.data = { number, text, author };
    }
  }
});

使用 arguments 後,可以取得所有的拋出數據。

監聽子組件的原生事件

組件上的事件有分成兩個系統,一個是 自定義事件,一個是 原生事件,若要在組件上監聽 原生事件,可以透過 v-on 添加 .native 修飾符,監聽子組件上的原生事件。

透過 native,可以對根元素監聽 click 事件。

Vue.component("my-component", {
  template: `
  <div class="child">
    <span>這是一個子組件</span>
    <button>父組件 count 加一</button>
  </div>`
});

const vm = new Vue({
  el: ".app",
  data: {
    count: 0
  },
  template: `
<div class="app">
  <p>父組件總計數:{{ count }}</p>
  <my-component @click.native="count++" />
</div>`
});

以上為此次內容,感謝看到這裡的你,明天會說明在組件中使用 v-model


若是文中有任何錯誤、錯字、想討論的內容,歡迎各位大大不吝鞭笞指正、交流分享,筆者不慎感激 ✦ ✦ ✦

▶︎ 筆者 github:https://github.com/YUN-RU-TSENG
▶︎ 老王賣瓜之筆者另一篇鐵人:每天來點 CSS Specification

▶︎ 倘若不斷向深處扎根,似乎就能茁壯成長 - RM


參考資料:

  1. Vuejs.org 2.x
  2. 想入門,我陪你 Re Vue 重頭說起|Day 11:組件自定事件與通知 - YouTube
  3. $emit with multiple arguments loses data with inline listener utilizing $event · Issue #5527 · vuejs/vue · GitHub

上一篇
How to find your passion as a Software Developer - Evan You
下一篇
Vue component 上該如何使用 v-model ?
系列文
每天來點 Vue.js 吧30

尚未有邦友留言

立即登入留言