Vue基礎——將原生事件綁定到組件


前言: 

今天再次看了看vue文檔,又找到了知識盲區。

對於 將原生事件綁定到組件 ,文檔有講,別人也有寫博客。

但我還是想根據自己的理解把這一內容講清楚

總的來說,有三種方法

  • 使用native修飾符

  • 使用$emit分發事件

  • 使用$listeners

 

正文:

首先,show code

html中代碼
<
div id="app"> <my-button @click='handleClick'></my-button> </div>
js中代碼
Vue.component('my-button',{ template:`<button>點擊</button>` }) const vm = new Vue({ el:'#app', methods:{ handleClick(){ console.log('click') } } })

在這里,我們定義了my-button子組件,在父組件中引用了,然后我們想在上面綁定click事件,觸發handleClick回調。

那么,可以猜猜有沒有觸發?

——結果很容易猜到,沒有觸發,click沒有打印輸出。

為什么?

——因為vue有自己事件運行機制,my-button不是原生DOM元素,我們是無法直接給其綁定原生事件並觸發的

所以就有解決辦法一:使用native修飾符

我們僅需要在click后加上.native就可,如下

<div id="app">
    <my-button @click.native='handleClick'></my-button>
</div>

相當於我們會把事件放在原生button標簽上,此時事件便觸發,click就打印了。

雖然這個方法使用起來非常簡單,但是其存在局限性:它只會把事件放在子組件的根標簽上。

上面子組件的根標簽就是button,自然就觸發了。

但是某些情況下,將某些事件綁定在根標簽而非目標標簽時,是無法觸發事件的。如下情況:

<div id="app2">
    <my-input @focus.native='handleFocus'></my-input>
</div>
Vue.component('my-input',{ template:` <label for=""> label: <input type="text">
   </label> ` }) const vm2 = new Vue({ el:'#app2', methods:{ handleFocus(){ console.log('focus...') } } })

盡管我們使用了native修飾符,但是focus事件放在子組件根標簽——label標簽上,無法觸發該事件。

所以就有解決辦法二:使用$emit分發事件

Vue.component('my-input',{ template:` <label for=""> label: <input type="text" @focus='$emit("focus","子組件的value")'>
   </label>
 ` }) const vm2 = new Vue({ el:'#app2', methods:{ handleFocus(value){ console.log('focus...',value)   //focus... 子組件的value
 } } })

在子組件input標簽中綁定focus事件,其回調中使用$emit分發事件,使父組件事件觸發。

$emit()有兩個參數:

第一個參數為分發的事件名,在這里為focus,也可改別的,只需要與父組件中給子組件標簽上綁定的事件名一致即可

第二個參數為給父組件該事件傳的參數,我們在父組件中的該事件回調中就可接受到。所以我們一般想將子組件的數據傳給父組件,完成父子組件間的通信,就可使用$emit。

除了這個解決方法外,還有第三種:使用$listeners

$listeners 它是一個對象,里面包含了作用在這個組件上的所有監聽器。

Vue.component('my-input',{ template:` <label for=""> label: <input type="text" v-on='$listeners'>    
   </label>
 ` })

(其余代碼同上一個方法,故省略)

在input上使用 v-on="$listeners" ,就是將所有的事件監聽器指向這個input元素。

故也同樣能打印出,且value值為event對象。

相比起方法二,這個$listener的使用更加全面——

  若是方法二,再在子組件標簽上綁定多個事件,那就要在子組件進行相應的寫事件名進行$emit分發

  而這個方法,就已經將所有事件監聽綁在input元素上了,就不用再次設置

而且,你也可以再次設置,$listeners的使用是很靈活的

你可以自定義監聽器,或者覆蓋一些監聽器的行為。

在下面的代碼中,重寫了focus事件監聽器

Vue.component('my-input',{
 template:` <label for=""> label: <input type="text" v-on='inputListeners'>    
   </label>
 `, computed:{ inputListeners(){ return Object.assign({},this.$listeners,{ focus:(event)=>{ this.$emit('focus',event.target.value) } }) } } })

首先,我們得明白 v-on:xxx = fn 等價於 v-on={xxx:fn}  

(tips:這里不可寫為@={xxx:fn}。所有指令的縮寫 @ : # 等等,都是在其有參數的情況下使用)

其次,inputListeners是一個計算屬性,返回的是一個對象,是將 $listeners 和 你重寫的事件的對象 合並的對象

還有,在這里重寫監聽器,還是用到了$emit,每個監聽器都得到event對象,我們可以取出event.target.value傳給父組件

最后,父組件就能在focus時觸發事件,並得到子組件傳來的值了

 

拓展:

當我們使用 $attrs 和 $listeners 時,my-input就相當於一個完全透明包裹器了。

怎么理解這句話呢?

  ——前面我們已經知道了  v-on = $listeners  會把 所有父組件綁定到該子組件上的事件都放在該元素上

    而使用  v-bind = $attrs  會把 除在props中聲明了的,除style和class 的父傳子的參數 都放在該元素上。

那么 當我們同時使用兩者放到某個元素上時 ,就已經把父組件所有放在子組件標簽(my-input)上的屬性、事件, 全都放在了該元素

此時,事件調用、屬性獲取都不再有障礙, my-input 不就可以理解成是透明的了嘛~

 

總結:

三種方法都能使原生事件綁定到組件上,就寫法上當然是第一種最簡單,第三種更麻煩。但是只要理解了就都挺好寫的了。

但是在使用時,還是根據需求來,若是就是想綁定到組件的根標簽上,直接使用第一種即可。否則,便使用二或三。

 

參考自vue文檔:將原生事件綁定到組件

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM