跳至主要內容
Skip to content

Vue 修飾符有哪些

Vue 修飾符是 template 裡很常見的語法糖,例如 @click.stop@submit.preventv-model.trim。它們的目的不是增加新能力,而是把常見的 DOM 事件處理、表單處理、屬性綁定行為,用更宣告式的方式寫在模板上。

一句話回答:Vue 修飾符可以分成事件修飾符、鍵盤修飾符、滑鼠修飾符、v-model 修飾符、v-bind 修飾符與自訂 v-model 修飾符;底層通常會在編譯階段轉成事件包裝函數、事件選項、props 或 model modifiers。


一、 必考觀念

1. 修飾符是什麼?

修飾符是接在指令後面的特殊後綴:

vue
<button @click.stop="handleClick">Click</button>
<form @submit.prevent="submit">Submit</form>
<input v-model.trim="username" />

它讓模板更專注於「這個事件或綁定應該具備什麼行為」,避免把大量樣板程式塞進 methods。

例如:

vue
<form @submit.prevent="submit">
  ...
</form>

等價心智模型:

typescript
function onSubmit(event) {
  event.preventDefault()
  submit(event)
}

2. 常見修飾符分類

類型例子用途
事件修飾符.stop.prevent.self.once.capture.passive控制 DOM 事件行為
鍵盤修飾符.enter.esc.tab.delete限制特定鍵盤事件才觸發
系統鍵修飾符.ctrl.shift.alt.meta.exact限制組合鍵
滑鼠修飾符.left.right.middle限制滑鼠按鍵
v-model 修飾符.lazy.number.trim控制表單同步行為
v-bind 修飾符.prop.attr.camel控制 attribute / property 綁定
自訂 model 修飾符v-model.capitalize傳給子元件自行處理

二、 事件修飾符

1. .stop

阻止事件冒泡。

vue
<div @click="parentClick">
  <button @click.stop="buttonClick">Click</button>
</div>

等價於:

typescript
function onClick(event) {
  event.stopPropagation()
  buttonClick(event)
}

適合場景:卡片點擊進詳情,但卡片內的按鈕有自己的操作,不希望觸發外層卡片點擊。

2. .prevent

阻止瀏覽器預設行為。

vue
<form @submit.prevent="submit">
  ...
</form>

等價於:

typescript
event.preventDefault()

常用於表單提交、阻止 <a> 原生跳轉。

3. .self

只有事件目標是元素本身時才觸發,不包含子元素冒泡上來的事件。

vue
<div class="modal-mask" @click.self="close">
  <div class="modal">
    ...
  </div>
</div>

點遮罩關閉,點 modal 內容不關閉。

4. .once

事件只觸發一次。

vue
<button @click.once="init">Init</button>

這通常會轉成事件 listener option 或等價的一次性處理。

5. .capture

使用事件捕獲階段。

vue
<div @click.capture="captureClick">
  <button @click="buttonClick">Click</button>
</div>

一般事件流程是 capture -> target -> bubble。.capture 讓 handler 在捕獲階段執行。

6. .passive

告訴瀏覽器這個 listener 不會呼叫 preventDefault()

vue
<div @scroll.passive="onScroll" />

常用於 scroll/touch 事件,讓瀏覽器可以更放心地做滾動優化。

IMPORTANT

不要同時使用 .passive.prevent.passive 表示不會阻止預設行為,.prevent 則是要阻止預設行為,語意衝突。

7. 修飾符順序會影響結果

Vue 會按照你寫的順序產生對應處理。

vue
<button @click.stop.prevent="submit" />

表示先 stop,再 prevent。

通常差異不大,但像 .self.prevent 組合時要小心:

vue
<!-- 只阻止元素自身點擊的預設行為 -->
<a @click.self.prevent="handle" />

<!-- 先 prevent,再判斷 self -->
<a @click.prevent.self="handle" />

面試時可以補一句:修飾符不是純標籤,順序可能影響生成後的邏輯。


三、 鍵盤與系統鍵修飾符

1. 鍵盤修飾符

常見鍵盤修飾符:

vue
<input @keyup.enter="submit" />
<input @keyup.esc="cancel" />
<input @keydown.tab="handleTab" />
<input @keyup.delete="remove" />

常見別名:

修飾符說明
.enterEnter
.tabTab
.deleteDelete 或 Backspace
.escEscape
.spaceSpace
.upArrowUp
.downArrowDown
.leftArrowLeft
.rightArrowRight

它的心智模型是:

typescript
function onKeyup(event) {
  if (event.key !== 'Enter') {
    return
  }

  submit(event)
}

2. 系統鍵修飾符

系統鍵包含:

vue
<button @click.ctrl="selectMultiple">Select</button>
<button @click.shift="selectRange">Select Range</button>
<button @click.meta="openCommand">Command</button>

常見系統鍵:

修飾符對應
.ctrlCtrl
.shiftShift
.altAlt
.metamacOS Command / Windows Win

例如:

vue
<input @keyup.ctrl.enter="submit" />

代表 Ctrl + Enter 時才提交。

3. .exact

.exact 用來限制「只能有指定的系統鍵」。

vue
<!-- Ctrl 被按下即可,其他系統鍵也可同時存在 -->
<button @click.ctrl="save">Save</button>

<!-- 只有 Ctrl 被按下才觸發,不能同時按 Shift / Alt / Meta -->
<button @click.ctrl.exact="save">Save</button>

<!-- 沒有任何系統鍵被按下才觸發 -->
<button @click.exact="normalClick">Click</button>

四、 滑鼠修飾符

滑鼠按鍵修飾符:

vue
<button @click.left="leftClick">Left</button>
<button @click.right="rightClick">Right</button>
<button @click.middle="middleClick">Middle</button>
修飾符說明
.left滑鼠左鍵
.right滑鼠右鍵
.middle滑鼠中鍵

右鍵通常也會搭配 .prevent 阻止預設選單:

vue
<div @contextmenu.prevent="openMenu" />

五、 v-model 修飾符

1. .lazy

預設 v-model 在文字輸入時使用 input 事件,使用者每輸入一次就同步。

vue
<input v-model="keyword" />

.lazy 改成在 change 時同步,通常是失焦或確認後。

vue
<input v-model.lazy="keyword" />

適合不想每個字都更新的場景。

2. .number

嘗試把輸入值轉成 number。

vue
<input v-model.number="age" />

要注意:它不是完整驗證。空字串、非法數字、特殊輸入仍要自己處理。

3. .trim

自動去掉字串前後空白。

vue
<input v-model.trim="username" />

適合帳號、關鍵字、email 這類常見輸入。

4. 自訂元件 v-model modifiers

Vue 也支援自訂 v-model 修飾符:

vue
<BaseInput v-model.capitalize="name" />

子元件可以讀到 modifiers,並自行決定如何處理。

在 Vue 3.4+,常見寫法是搭配 defineModel

vue
<script setup lang="ts">
const [model, modifiers] = defineModel<string>()

function update(value: string) {
  if (modifiers.capitalize) {
    model.value = value.charAt(0).toUpperCase() + value.slice(1)
    return
  }

  model.value = value
}
</script>

如果不用 defineModel,也可以透過 modelModifiers 這類 props 設計處理。

延伸讀:v-model 雙向綁定的原理


六、 v-bind 修飾符

1. .prop

強制作為 DOM property 綁定。

vue
<div :text-content.prop="message" />

一般情況 Vue 會自動判斷 attribute/property,但 .prop 可以明確指定。

2. .attr

強制作為 attribute 綁定。

vue
<div :some-prop.attr="value" />

適合你想明確輸出 HTML attribute,而不是設定 DOM property 的場景。

3. .camel

把 kebab-case attribute 名轉成 camelCase。

vue
<svg :view-box.camel="viewBox" />

在 DOM 模板或特殊 attribute 場景中可能用到。SFC template 裡通常比較少需要手動使用。


七、 底層原理

1. 事件修飾符如何實現?

Vue compiler 會把事件修飾符轉成對應 helper。

概念上:

vue
<button @click.stop.prevent="submit">Submit</button>

會變成類似:

typescript
withModifiers(submit, ['stop', 'prevent'])

執行時 wrapper 會先處理:

typescript
event.stopPropagation()
event.preventDefault()

再呼叫你的 submit

2. 鍵盤修飾符如何實現?

鍵盤修飾符會變成 key guard。

vue
<input @keyup.enter="submit" />

概念上:

typescript
withKeys(submit, ['enter'])

只有符合指定 key 時才執行 handler。

3. .once.capture.passive 如何實現?

這類修飾符更接近 addEventListener 的 options:

typescript
el.addEventListener('click', handler, {
  once: true,
  capture: true,
  passive: true,
})

Vue 會透過事件名稱或 patch event 邏輯把它們轉成對應 listener options。

4. v-model 修飾符如何實現?

原生表單上的 v-model.trim.number.lazy 會影響事件與取值處理。

自訂元件上的修飾符則會傳給子元件,讓子元件決定怎麼處理。

所以修飾符不只有一種實現方式,要看它修飾的是 v-onv-model 還是 v-bind


八、 追問題庫

Q1:.stop.prevent 差在哪?

.stop 是阻止事件冒泡,對應 event.stopPropagation()

.prevent 是阻止預設行為,對應 event.preventDefault()

例如表單提交通常用 .prevent,內層按鈕不想觸發外層點擊通常用 .stop

Q2:.self.stop 差在哪?

.stop 會阻止事件繼續往外冒泡。

.self 不阻止冒泡,只是限制 handler 只有在 event.target === event.currentTarget 時才執行。

Q3:為什麼 .passive 不應該和 .prevent 一起用?

因為 .passive 告訴瀏覽器這個 listener 不會阻止預設行為,.prevent 卻要阻止預設行為。兩者語意衝突。

Q4:v-model.number 一定會得到 number 嗎?

不一定。

.number 會嘗試轉型,但它不是完整驗證。表單輸入仍可能有空字串或無法轉換的值。重要資料仍應做顯式驗證。

Q5:Vue 2 的 .native 還能用嗎?

Vue 2 常見 .native 用來監聽子元件根元素的原生事件。

Vue 3 移除了 .native。如果事件沒有被子元件宣告在 emits 裡,通常會作為 fallthrough listener 傳到子元件根元素。實務上更推薦明確定義 emits,讓元件事件 API 清楚。

Q6:.sync 還能用嗎?

Vue 2 的 .sync 常用於父子雙向更新。

Vue 3 移除 .sync,改用更統一的 v-model:propName

vue
<Dialog v-model:open="opened" />

這本質上是:

vue
<Dialog
  :open="opened"
  @update:open="opened = $event"
/>

九、 實作題

題目:解釋這段模板中的修飾符

vue
<template>
  <form @submit.prevent="submit">
    <input
      v-model.trim="username"
      @keyup.enter.exact="submit"
    />

    <button
      type="button"
      @click.stop.once="send"
    >
      Send
    </button>
  </form>
</template>

可以這樣回答:

  • @submit.prevent:阻止表單預設提交刷新頁面,改由 submit 處理。
  • v-model.trim:同步輸入值時去掉前後空白。
  • @keyup.enter.exact:只有按 Enter 且沒有其他系統鍵時才提交。
  • @click.stop.once:點擊按鈕時阻止事件冒泡,且 send 只觸發一次。

十、 資深視角

1. 修飾符是模板可讀性工具

修飾符讓常見事件行為直接出現在模板上:

vue
@submit.prevent
@click.stop
@keyup.enter

讀者不用跳到 methods 裡才知道有沒有 preventDefault()stopPropagation()。這是它最大的價值。

2. 不要把複雜邏輯塞進修飾符組合

修飾符適合處理通用、簡單、可預期的 DOM 行為。

如果條件變複雜,應該回到明確函數:

vue
<button @click="handleClick">Click</button>
typescript
function handleClick(event: MouseEvent) {
  if (shouldIgnore(event)) {
    return
  }

  submit()
}

3. 理解修飾符能幫你理解 compiler

修飾符不是 runtime 魔法。很多修飾符會在 compiler 階段被轉成 helper、事件選項或 props。

理解這點後,就更容易接上:Vue Compiler 的實現原理


總結

類型常見修飾符
事件.stop.prevent.self.once.capture.passive
鍵盤.enter.tab.delete.esc.space.up.down.left.right
系統鍵.ctrl.shift.alt.meta.exact
滑鼠.left.right.middle
v-model.lazy.number.trim、自訂 modifiers
v-bind.prop.attr.camel

一句完整的面試回答可以是:

Vue 修飾符是模板指令的語法糖,常見類型有事件修飾符、鍵盤修飾符、系統鍵修飾符、滑鼠修飾符、v-model 修飾符和 v-bind 修飾符。事件修飾符像 .stop.prevent 會被轉成事件包裝邏輯;.once.capture.passive 接近 addEventListener options;鍵盤修飾符會生成 key guard;v-model.trim.number.lazy 會影響表單取值與同步時機。它們的核心價值是讓常見 DOM 行為以宣告式方式留在模板中。

延伸閱讀