修饰符
修饰符是 Vue 提供的指令语法糖,用来声明事件、表单绑定或按键行为的附加规则。
一、事件修饰符
事件修饰符写在事件名后面:
<button @click.stop="handleClick">提交</button>
<form @submit.prevent="handleSubmit">...</form>
.self 和冒泡
<!-- 点击 div 自身才触发;点击 button 不触发此处理器 -->
<div @click.self="handleClick">
<button>按钮</button>
</div>
.self 判断的是事件的原始目标 target,不是当前正在执行处理器的元素 currentTarget。因此,子元素冒泡上来的事件不会通过 .self。
修饰符顺序
修饰符会按书写顺序生成处理逻辑,因此顺序可能影响结果:
@click.prevent.self="handleClick"
@click.self.prevent="handleClick"
可以近似理解为:
// .prevent.self:先阻止默认行为,再判断是否为元素自身
event.preventDefault()
if (event.target !== event.currentTarget) return
handleClick(event)
// .self.prevent:先判断目标,再阻止默认行为
if (event.target !== event.currentTarget) return
event.preventDefault()
handleClick(event)
所以,前者会阻止子元素点击的默认行为,后者只会阻止元素自身点击的默认行为。
.passive 与 .prevent 不应同时使用。前者明确声明不会阻止默认行为,浏览器可能忽略后者并输出警告。
二、按键修饰符
按键修饰符用于键盘事件:
<input @keyup.enter="submit" />
<input @keyup.esc="cancel" />
<input @keyup.ctrl.enter="submitWithCtrl" />
常用按键修饰符:
.enter、.tab、.delete、.esc、.space、.up、.down、.left、.right
.ctrl、.alt、.shift、.meta:系统修饰键
.exact:要求系统修饰键组合完全匹配
<!-- 只允许 Ctrl + Enter;Ctrl + Shift + Enter 不触发 -->
<input @keyup.ctrl.enter.exact="submit" />
按键修饰符本质上是对 KeyboardEvent 的条件判断:Vue 会检查 event.key(或对应按键别名)以及 ctrlKey、altKey、shiftKey、metaKey 等属性,条件满足后才调用处理函数。
三、表单修饰符
<input v-model.trim="name" />
<input v-model.number="age" />
<input v-model.lazy="keyword" />
.lazy 是事件时机切换,不是防抖,也不会延迟一段固定时间后再执行。
多个修饰符可以组合:
<input v-model.trim.number="price" />
四、自定义 v-model 修饰符(Vue 3)
组件上的修饰符不会自动改变值,Vue 会把它们作为 prop 传给组件,由组件决定如何处理:
<!-- Parent.vue -->
<Input v-model.capitalize="text" />
对于默认的 v-model,子组件接收 modelModifiers:
<!-- Input.vue -->
<script setup>
const props = defineProps({
modelValue: String,
modelModifiers: {
default: () => ({})
}
})
const emit = defineEmits(['update:modelValue'])
function handleInput(event) {
let value = event.target.value
if (props.modelModifiers.capitalize) {
value = value.charAt(0).toUpperCase() + value.slice(1)
}
emit('update:modelValue', value)
}
</script>
<template>
<input :value="modelValue" @input="handleInput" />
</template>
带参数的 v-model 使用对应名称的修饰符 prop:
<UserName v-model:first.capitalize="firstName" />
组件中接收 firstModifiers,而不是 modelModifiers。
五、修饰符的实现原理
修饰符主要在模板编译阶段被转换成普通 JavaScript 逻辑或原生监听器配置。以事件绑定为例:
<button @click.stop.prevent="save">保存</button>
可以近似看成:
button.addEventListener('click', (event) => {
event.stopPropagation()
event.preventDefault()
save(event)
})
而 .capture、.once、.passive 等会进一步影响 addEventListener 的 options:
button.addEventListener('click', handler, {
capture: true,
once: true,
passive: true
})
v-model 修饰符则会影响“读取事件值”和“写回响应式数据”的过程:
用户输入
↓
读取 input/change 事件的 value
↓
trim / number 等转换
↓
更新绑定的 ref 或 reactive 属性
因此,修饰符不是浏览器 API,也不是独立的运行时魔法,而是 Vue 在编译或运行时帮助我们补上的条件判断、事件调用和监听器配置。
六、常见误区
.stop 只影响事件传播,不会阻止链接跳转或表单提交;需要阻止默认行为时使用 .prevent。
.prevent 只对有默认行为的事件有意义,不能代替 .stop。
.self 不等于“只监听当前元素注册的事件”,它只检查 event.target 是否就是当前元素。
.once 是监听器最多执行一次,不是绑定的数据只更新一次。
.passive 和 .prevent 语义冲突,不要组合使用。
.lazy 不是防抖;需要防抖时应使用防抖函数或专门的 composable。