iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 25
0
自我挑戰組

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

Vue 透過 `$listeners` 在子組件特定元素上監聽原生事件

tags: Vuejs

監聽子組件原生事件 ✐

當父組件需要接收子組件訊息、數據時,可以透過在子組件 自定義事件 達成,除此之外若是需要監聽子組件 根元素 原生事件,可以透過 v-on 配合 .native 修飾符監聽:

.native

監聽子組件根元素的 原生事件

今天創建一個 my-component 子組件,父組件對 v-on 添加 .native 後可監聽子組件根元素原生事件。

下方簡單範例監聽子組件根元素 click 事件,父組件一旦監聽到該事件,便更改數據

子組件

Vue.component('my-component', {
  template: `<div>這是子組件
  <button>BUTTON</button>
</div>`
})

父組件

const vm = new Vue({
  el: '#app',
  data: {
    text: '原始值'
  },
  template: `
<div class="app">
  <p>父組件數據: {{ text }}</p>
  <my-component @click.native="handler"/>
</div>`,
  methods: {
    handler(event) {
      this.text = '修改了'
      console.log('event.target', event.target)
      console.log('event.currentTarget', event.currentTarget)
    },
  },
})

根元素 不必要 為事件物件被指派到的 Event target 元素,例如今天的 click 事件的 Event targetbutton,但事件流 ^規範定義^ 通過了根元素,事件仍然會被監聽,就如同原生 DOM 事件一樣。

透過 $listeners 在子組件特定元素上監聽原生事件

除了在子組件中監聽 根元素 原生事件,也可以透過 $listeners 物件,將父組件的所有事件監聽器指向子組件的特定元素。

$listeners 介紹

唯讀類型的物件,是一個包含了 parent-scope 所有事件監聽器的物件。

子組件

透過 created 階段,打印 $listeners,可以看見如下方的物件,裡面包涵 parent-scope 的所有事件監聽器。

Vue.component('my-component', {
  template: `<div>
<label for="name" >name: <input type="text" id="name"></label>
</div>`,
  created() {
    console.log(this.$listeners)
  },
})

父組件

父組件於子組件對 focusclick 兩事件進行監聽,對應 $listeners 中的兩個事件監聽器。

const vm = new Vue({
  el: '#app',
  template: `
<div class="app">
  <my-component  @focus="handler('focus')" @click="handler('click')"
/>
</div>`,
  methods: {
    handler(event) {
      console.log(event)
    },
  },
})

今天若是要在子組件特定元素上監聽原生事件,變使用 v-on="$listener" 監聽子組件元素。

Vue.component('my-component', {
  template: `<div>
<label for="name" >name: <input type="text" id="name"></label>
</div>`
})
const vm = new Vue({
  el: '#app',
  template: `
<div class="app">
  <my-component  @focus="handler('focus')" @click="handler('click')"
/>
</div>`,
  methods: {
    handler(event) {
      console.log(event)
    },
  },
})

單純使用 .native 監聽根元素,無法監聽 focus 事件,由於 focus 不會 bubble,所以對 label 監聽 focus 無效,在此若是要將父組件的 focus 事件監聽器指向子組件的特定元素,可以改採 $listeners

對子組件添加 v-on="$listeners"

<label for="name" >name: <input type="text" id="name" v-on="$listeners"></label>

相當於:

<label for="name" >name: <input type="text" id="name" v-on="{click: function(){...}, focus: function(){...}
}"></label>

並且移除父組件上的 .native,由於 $listeners 不包含使用 .native 的事件監聽器。

此時子組件的 input 元素便會開始監聽 focusclick 原生事件。

focus 事件沒有 bubble 補充

This event type is similar to focusin, but is dispatched after focus is shifted, and does not bubble.

focus 事件沒有 bubble,於是上述範例情境下針對 label 監聽 focus 沒有作用。

結語

今天提及 $listeners 物件,該物件包含 parent-scope 中的事件監聽器,透過該物件,我們可以靈活地在子組件特定元素上監聽原生事件。


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

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

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


參考資料:

  1. Vuejs.org 2.x
  2. vue/lifecycle.js at dev · vuejs/vue · GitHub
  3. Document Object Model (DOM) Level 3 Events Specification
  4. Custom Events — Vue.js

上一篇
Vue .sync Modifier
下一篇
Vue slot:編譯作用域、後備內容
系列文
每天來點 Vue.js 吧30

尚未有邦友留言

立即登入留言