使用v-on指令(简写为@)监听DOM事件。
<template>
<div>
<!-- 完整语法 -->
<button v-on:click="count++">点击次数: {{ count }}</button>
<!-- 简写语法 -->
<button @click="count++">点击次数: {{ count }}</button>
<!-- 调用方法 -->
<button @click="handleClick">点击我</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
const count = ref(0)
function handleClick() {
console.log('按钮被点击了')
count.value++
}
</script>
<template>
<div>
<button @click="count++">增加</button>
<button @click="count--">减少</button>
<button @click="say('hello')">打招呼</button>
<p>计数: {{ count }}</p>
</div>
</template>
<script setup>
import { ref } from 'vue'
const count = ref(0)
function say(message) {
alert(message)
}
</script>
<template>
<div>
<!-- 自动传入event -->
<button @click="handleClick">点击1</button>
<!-- 使用$event传入event -->
<button @click="handleClickWithArg('hello', $event)">点击2</button>
<!-- 箭头函数 -->
<button @click="(event) => handleEvent(event)">点击3</button>
</div>
</template>
<script setup>
function handleClick(event) {
console.log(event.target.tagName)
}
function handleClickWithArg(message, event) {
console.log(message, event.target)
}
function handleEvent(event) {
console.log(event)
}
</script>
Vue提供了事件修饰符来处理常见的DOM事件细节。
<template>
<div>
<!-- .stop 阻止事件冒泡 -->
<div @click="outerClick" style="padding:20px;background:#f0f0f0">
<button @click.stop="innerClick">阻止冒泡</button>
</div>
<!-- .prevent 阻止默认行为 -->
<form @submit.prevent="handleSubmit">
<input type="text" v-model="message">
<button type="submit">提交</button>
</form>
<!-- .capture 使用捕获模式 -->
<div @click.capture="captureClick" style="padding:20px;background:#e0e0e0">
<button @click="normalClick">捕获模式</button>
</div>
<!-- .self 只有事件源是自身才触发 -->
<div @click.self="selfClick" style="padding:20px;background:#d0d0d0">
<button>自身触发</button>
</div>
<!-- .once 只触发一次 -->
<button @click.once="onceClick">只触发一次</button>
<!-- .passive 提升滚动性能 -->
<div @scroll.passive="onScroll" style="height:100px;overflow:auto">
<div style="height:500px">滚动区域</div>
</div>
</div>
</template>
<script setup>
function outerClick() {
console.log('外部点击')
}
function innerClick() {
console.log('内部点击')
}
function handleSubmit() {
console.log('表单提交')
}
function captureClick() {
console.log('捕获阶段触发')
}
function normalClick() {
console.log('冒泡阶段触发')
}
function selfClick() {
console.log('自身点击')
}
function onceClick() {
console.log('只触发一次')
}
function onScroll() {
console.log('滚动中...')
}
const message = ref('')
</script>
Vue提供了按键修饰符来监听特定的键盘事件。
<template>
<div>
<!-- 按键别名 -->
<input @keyup.enter="onEnter" placeholder="按回车键触发">
<input @keyup.tab="onTab" placeholder="按Tab键触发">
<input @keyup.delete="onDelete" placeholder="按删除键触发">
<!-- 系统修饰键 -->
<button @click.ctrl="onCtrlClick">Ctrl+点击</button>
<button @click.shift="onShiftClick">Shift+点击</button>
<!-- 精确修饰符 -->
<button @click.ctrl.exact="onCtrlExact">仅Ctrl+点击</button>
<!-- 鼠标按键修饰符 -->
<button @click.right="onRightClick">右键点击</button>
<button @click.middle="onMiddleClick">中键点击</button>
</div>
</template>
<script setup>
function onEnter() {
console.log('回车键被按下')
}
function onTab() {
console.log('Tab键被按下')
}
function onDelete() {
console.log('删除键被按下')
}
function onCtrlClick() {
console.log('Ctrl+点击')
}
function onShiftClick() {
console.log('Shift+点击')
}
function onCtrlExact() {
console.log('仅Ctrl+点击')
}
function onRightClick() {
console.log('右键点击')
}
function onMiddleClick() {
console.log('中键点击')
}
</script>
方法处理器可以接收参数,也可以自动接收事件对象。
<template>
<div>
<!-- 自动传入事件对象 -->
<button @click="handleClick">点击1</button>
<!-- 手动传入参数 -->
<button @click="handleClickWithArg('hello')">点击2</button>
<!-- 同时传入参数和事件对象 -->
<button @click="handleClickWithArgAndEvent('hello', $event)">点击3</button>
<!-- 在方法中访问组件数据 -->
<button @click="increment">增加计数: {{ count }}</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
const count = ref(0)
function handleClick(event) {
console.log('事件对象:', event)
console.log('目标元素:', event.target)
}
function handleClickWithArg(message) {
console.log('消息:', message)
}
function handleClickWithArgAndEvent(message, event) {
console.log('消息:', message)
console.log('事件对象:', event)
}
function increment() {
count.value++
console.log('当前计数:', count.value)
}
</script>
Vue的事件处理系统基于原生DOM事件机制,但提供了更高级的抽象和便利性:
以下是一个完整的表单验证和交互示例:
<template>
<div class="form-container">
<h3>用户注册表单</h3>
<form @submit.prevent="handleSubmit">
<div class="form-group">
<label for="username">用户名:</label>
<input
id="username"
type="text"
v-model="form.username"
@input="validateUsername"
@blur="markUsernameTouched"
:class="{ error: errors.username }"
>
<span v-if="errors.username" class="error-message">{{ errors.username }}</span>
</div>
<div class="form-group">
<label for="email">邮箱:</label>
<input
id="email"
type="email"
v-model="form.email"
@input="validateEmail"
@keyup.enter="focusNextField"
:class="{ error: errors.email }"
>
<span v-if="errors.email" class="error-message">{{ errors.email }}</span>
</div>
<div class="form-group">
<label for="password">密码:</label>
<input
id="password"
type="password"
v-model="form.password"
@input="validatePassword"
@keyup.enter="focusNextField"
:class="{ error: errors.password }"
>
<span v-if="errors.password" class="error-message">{{ errors.password }}</span>
</div>
<div class="form-actions">
<button
type="submit"
:disabled="!isFormValid"
@mouseenter="onButtonHover"
@mouseleave="onButtonLeave"
>
注册
</button>
<button
type="button"
@click="resetForm"
@keyup.escape="resetForm"
>
重置
</button>
</div>
</form>
<div v-if="submitted" class="success-message">
注册成功!欢迎 {{ form.username }}
</div>
</div>
</template>
<script setup>
import { ref, reactive, computed } from 'vue'
const form = reactive({
username: '',
email: '',
password: ''
})
const errors = reactive({
username: '',
email: '',
password: ''
})
const submitted = ref(false)
const touchedFields = reactive({
username: false,
email: false,
password: false
})
const isFormValid = computed(() => {
return !errors.username && !errors.email && !errors.password &&
form.username && form.email && form.password
})
function validateUsername() {
if (!form.username) {
errors.username = '用户名不能为空'
} else if (form.username.length < 3) {
errors.username = '用户名至少3个字符'
} else {
errors.username = ''
}
}
function validateEmail() {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
if (!form.email) {
errors.email = '邮箱不能为空'
} else if (!emailRegex.test(form.email)) {
errors.email = '请输入有效的邮箱地址'
} else {
errors.email = ''
}
}
function validatePassword() {
if (!form.password) {
errors.password = '密码不能为空'
} else if (form.password.length < 6) {
errors.password = '密码至少6个字符'
} else {
errors.password = ''
}
}
function markUsernameTouched() {
touchedFields.username = true
validateUsername()
}
function focusNextField(event) {
const fields = ['username', 'email', 'password']
const currentIndex = fields.indexOf(event.target.id)
if (currentIndex < fields.length - 1) {
document.getElementById(fields[currentIndex + 1])?.focus()
}
}
function onButtonHover() {
console.log('鼠标悬停在按钮上')
}
function onButtonLeave() {
console.log('鼠标离开按钮')
}
function handleSubmit() {
if (isFormValid.value) {
console.log('提交表单:', form)
submitted.value = true
// 这里可以发送到服务器
}
}
function resetForm() {
Object.keys(form).forEach(key => {
form[key] = ''
errors[key] = ''
touchedFields[key] = false
})
submitted.value = false
}
</script>
<style scoped>
.form-container {
max-width: 400px;
margin: 0 auto;
padding: 20px;
}
.form-group {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
input {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
}
input.error {
border-color: #f44336;
}
.error-message {
color: #f44336;
font-size: 12px;
margin-top: 5px;
}
.form-actions {
display: flex;
gap: 10px;
margin-top: 20px;
}
button {
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:disabled {
background-color: #ccc;
cursor: not-allowed;
}
.success-message {
margin-top: 20px;
padding: 10px;
background-color: #4caf50;
color: white;
border-radius: 4px;
}
</style>
A: 常见原因包括:方法名拼写错误、方法未定义、事件绑定语法错误等。
A: 使用.stop修饰符或在事件处理函数中调用event.stopPropagation()。
A: 可以链式使用修饰符,如@click.stop.prevent="handler"。
创建一个计数器组件,包含以下功能:
创建一个可拖拽排序的待办事项列表: