# 前言
今天來聊聊 Vue 中事件的寫法。
# 基本語法
如果你有看過怎麼在 HTML 寫事件,像是 onclick="functionToExecute()"
之類的,那你對於 Vue 提供的是鍵語法應該會很熟悉。
基礎與法式使用 v-on:[事件名稱]
,例如 v-on:click="func()"
、 v-on:keydown="func()"
。
這個指令也有縮寫: @
。也就是說可以寫成 @click="func()"
、 @keydown="func()"
在模板上看起來會長這樣:
<button @click="plus()">+</button> |
# 事件的參數
說到事件,不得不提到事件的參數,在 方法
的篇章已經有聊到 v-on 指令常會搭配 「方法」一起使用。
當時沒有講到的是, v-on 綁定的 method 支援一個特殊的參數 $event
,可以讓我們取得事件的 event 物件。
這個物件在傳統 JS 來說可能會寫個 e
來接這個參數,像這樣:
const $btn = document.getElementById("btn"); | |
$btn.addEventListener("click", function (e) { | |
console.log(e); | |
}); |
而如果是 v-on 指令,則可以這樣寫:
<div id="app"> | |
<!-- 一定要是 $event 不能是其他值 --> | |
<a href="https://google.com.tw" @click="plus($event)">{{data}}</a> | |
</div> |
Vue.createApp({ | |
setup() { | |
const data = Vue.ref(0); | |
const plus = (e) => { | |
// 模板有寫 $event 這裡就能抓到了 | |
e.preventDefault(); | |
data.value++; | |
}; | |
return { data, plus }; | |
}, | |
}).mount("#app"); |
不過如果方法中需要帶其他參數,就要記得,模板寫的參數位置,跟方法中的參數位置必須一樣:
<div id="app"> | |
<!-- 因為在實體中寫的參數,第一個是接「事件參數」,第二個是某個資料值,所以兩者順序不能換 --> | |
<a href="https://google.com.tw" @click="plus($event , 5)">{{data}}</a> | |
</div> |
Vue.createApp({ | |
setup() { | |
const data = Vue.ref(0); | |
const plus = (e, value) => { | |
// 模板有寫 $event 這裡就能抓到了 | |
e.preventDefault(); | |
data.value = value; | |
}; | |
return { data, plus }; | |
}, | |
}).mount("#app"); |
# 修飾子
# 常見通用修飾子
針對一些特殊的事件操作,Vue 支援一些特殊的修飾子。
.stop
.prevent
.capture
.self
.once
一個一個來介紹
# .stop
他就是 .stopPropagation()
,阻擋事件冒泡用的。
我們都像是這種 HTML 結構,如果在 .inner
綁點擊事件,點了他之後,同時也會讓 .outer
也觸發點擊事件。
一般我們就會使用 e.stopPropagation()
來阻擋事件冒泡。
<div class="outer"> | |
這是外面 | |
<div class="inner">這是裡面</div> | |
</div> |
因為很常用,所以 Vue 提供了 .stop
語法糖讓我們使用,用法就是直接在 event 名稱接 .stop
即可。
<div id="app"> | |
<div class="outer" @click="console.log('outer')"> | |
這是外面 | |
<div class="inner" @click.self="console.log('inner')">這是裡面</div> | |
</div> | |
</div> |
# .prevent
如果你懂 JS ,可能也不用多說什麼了,這個修飾子就是 e.preventDefault()
,阻擋預設行為用的。
例如上面在講 $event 的範例,methods 接了 a 標籤的點擊事件後,做了 e.preventDefault()
的行為。
事實上在 Vue 中可以直接這樣寫:
<a href="https://google.com.tw" @click.prevent="console.log('hello!!!')" | |
>點我啊</a | |
> |
而且 Vue 的事件就像 jQuery 語法一樣,是可以接續著寫下去的:
<a href="https://google.com.tw" @click.stop.prevent="console.log('hello!!!')" | |
>點我啊</a | |
> |
# .capture
我們再把這個結構請出來用:
<div class="outer"> | |
這是外面 | |
<div class="inner">這是裡面</div> | |
</div> |
一般來說,如果沒有做任何設定,點擊 inner 時,會先觸發 inner 的 click ,才會觸發 outer 的 click 事件。
這是預設的 JS 事件冒泡的機制。
不過如果我們在「外層」加一個 .capture,那整個操作行為就會反過來,變成先觸發 outer ,才觸發 inner
。
<div class="outer" @click.capture="console.log("hello!")"> | |
這是外面 | |
<div class="inner" @click="console.log('裡面!')"> | |
這是裡面 | |
</div> | |
</div> |
# .self
self 中文是 自己。
所以當事件加上這個修飾子,就會限定只點自己才有效。
剛剛在 .stop 的地方有講到事件冒泡,點擊內層結構也會觸發外層結構的事件。
以常見的「燈箱效果」來說,如果這個結構是一個燈箱效果,外面那層是整個黑幕,裡面才是實際的燈箱,當燈箱跳出來的時候,我們希望點擊黑幕才觸發關閉事件,點到內容不做任何事,那我們就可以直接在外面那層加上 .self
<div class="outer"> | |
這是外面 | |
<div class="inner">這是裡面</div> | |
</div> |
好啦… 我知道我說得很清楚,你卻聽得很模糊,直接進去看程式,試試看把 .self 拿掉,打開燈箱,然後點點看「這是裡面」,看會有什麼變化吧。
可能有人會把 .self 跟 .stop 搞混,會以為 在內層設定 .self 可以阻止冒泡。
這裡必須強調 .self 不是拿來阻止冒泡行為的!!!!
# .once
這個就跟 v-once 的概念很像, v-once 是資料綁到模板之後,資料怎麼更動後都不會再重新渲染。
.once 指令,就是讓事件只觸發一次,然後就不會再有然後了。
.once 跟 v-once 不太一樣的地方是,之前講 v-once 的時候,示範畫面沒有異動,但資料還是有被異動。
不過.once 控制的是「事件」,換言之是讓那個 function 只觸發一次,所以如果裡面有改動資料,就也只會改動那一次,之後怎麼執行事件,事件的函式就是不會再執行。
<div id="app"> | |
<button @click.once="plus()">{{count}}</button> | |
</div> |
Vue.createApp({ | |
setup() { | |
const count = Vue.ref(0); | |
const plus = () => { | |
count.value++; | |
console.log(count.value); | |
}; | |
return { count, plus }; | |
}, | |
}).mount("#app"); |
# 鍵盤類型修飾子
除了通用修飾子,針對鍵盤, Vue 也提供一些不錯用的修飾子
例如執行 @keydown 事件時,只想讓 enter 鍵才執行某個行為,則可以寫
<div @keydown.enter="console.log('enter')"></div> |
其他常見的還有這些:
.enter
.tab
.delete
.esc
.space
.ctrl
.shift