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}
其次,inputListeners是一個計算屬性,返回的是一個物件,是將$listeners 和 你重寫的事件的物件 合併的物件
還有,在這裡重寫監聽器,還是用到了$emit,每個監聽器都得到event物件,我們可以取出event.target.value傳給父元件
最後,父元件就能在focus時觸發事件,並得到子元件傳來的值了
總結:三種方法都能使原生事件繫結到元件上,就寫法上當然是第一種最簡單,第三種更麻煩。但是隻要理解了就都挺好寫的了。
但是在使用時,還是根據需求來,若是就是想繫結到元件的根標籤上,直接使用第一種即可。否則,便使用二或三。