这一节了解一下Vue3中的处理用户交互,处理用户交互实际就是对用户操作事件的监听和处理。在Vue中,使用v-on指令来进行事件的监听和处理,简单总结如下:
API
- @click / v-on:click
含义:点击事件绑定 作用:监听用户单击操作
javascript
<template>
<button @click="handleClick">点击测试</button>
</template>
<script setup>
const handleClick = () => uni.showToast({ title: '点击了' })
</script>
- 事件对象 $event /e
含义:系统自动传入事件对象 作用:获取输入值、鼠标位置、按键信息等
javascript
<template>
<input @input="onInput" placeholder="输入查看" />
</template>
<script setup>
const onInput = (e) => console.log(e.target.value)
</script>
- @dblclick 双击事件
含义:鼠标快速双击 作用:编辑、打开、快速操作
javascript
<template>
<view @dblclick="handleDblClick">双击我</view>
</template>
<script setup>
const handleDblClick = () => uni.showToast({ title: '双击触发' })
</script>
- @mousedown / @mouseup
含义:鼠标按下 / 抬起 作用:拖拽、长按、按钮按下效果
javascript
<template>
<view @mousedown="down" @mouseup="up">按下/抬起测试</view>
</template>
<script setup>
const down = () => console.log('按下')
const up = () => console.log('抬起')
</script>
- @mousemove 鼠标移动
含义:鼠标在元素内移动 作用:跟随效果、画板、坐标获取
javascript
<template>
<view class="box" @mousemove="move"></view>
</template>
<script setup>
const move = (e) => console.log('x:', e.clientX, 'y:', e.clientY)
</script>
<style>.box{width:300px;height:200px;background:#f5f5f5;}</style>
- @mouseover / @mouseout
含义:鼠标移入 / 移出 作用:悬浮显示、提示框
javascript
<template>
<view @mouseover="over" @mouseout="out">悬浮测试</view>
</template>
<script setup>
const over = () => console.log('移入')
const out = () => console.log('移出')
</script>
- @focus 输入框聚焦
含义:输入框被激活 作用:聚焦动画、清空提示、键盘弹出
javascript
<template>
<input @focus="onFocus" placeholder="点我聚焦" />
</template>
<script setup>
const onFocus = () => uni.showToast({ title: '已聚焦' })
</script>
- @blur 输入框失焦
含义:输入框失去焦点 作用:校验输入、隐藏键盘
javascript
<template>
<input @blur="onBlur" placeholder="失焦测试" />
</template>
<script setup>
const onBlur = () => uni.showToast({ title: '已失焦' })
</script>
- @change 内容改变
含义:输入完成后内容变化 作用:选择器、输入完成校验
javascript
<template>
<input @change="change" placeholder="输完离开触发" />
</template>
<script setup>
const change = (e) => console.log('改变:', e.target.value)
</script>
- @keyup 按键抬起
含义:键盘按键松开 作用:搜索、回车提交、快捷键
javascript
<template>
<input @keyup="keyup" placeholder="按键测试" />
</template>
<script setup>
const keyup = (e) => console.log('按键:', e.key)
</script>
- @keyup.enter 回车
含义:按下回车键 作用:表单提交、搜索、发送消息
javascript
<template>
<input @keyup.enter="enter" placeholder="按回车提交" />
</template>
<script setup>
const enter = () => uni.showToast({ title: '回车提交' })
</script>
- @keyup.esc 退出键
含义:按下 ESC 作用:关闭弹窗、取消操作
javascript
<template>
<input @keyup.esc="esc" placeholder="按ESC关闭" />
</template>
<script setup>
const esc = () => uni.showToast({ title: 'ESC 关闭' })
</script>
- .stop 阻止冒泡
含义:阻止事件向上传递 作用:子点击不触发父点击
javascript
<template>
<view @click="parent">
父区域
<view @click.stop="child">子区域(不冒泡)</view>
</view>
</template>
<script setup>
const parent = () => console.log('父')
const child = () => console.log('子')
</script>
- .prevent 阻止默认行为
含义:阻止浏览器 / 系统默认动作 作用:阻止页面跳转、表单默认提交
javascript
<template>
<view @click.prevent="click">阻止默认行为</view>
</template>
<script setup>
const click = () => console.log('自定义处理')
</script>
- .capture 捕获模式
含义:事件由外向内先触发 作用:父优先拦截事件
javascript
<template>
<view @click.capture="parent">父捕获</view>
</template>
<script setup>
const parent = () => console.log('父优先触发')
</script>
- .once 只执行一次
含义:事件只触发一次作用:红包、首次引导、防重复
javascript
<template>
<button @click.once="once">只触发一次</button>
</template>
<script setup>
const once = () => uni.showToast({ title: '触发成功' })
</script>
- .self 仅点击自身触发
含义:只有点击元素本身才执行 作用:点击遮罩关闭、点击空白关闭
javascript
<template>
<view @click.self="self">点击空白有效</view>
</template>
<script setup>
const self = () => console.log('点击自身才触发')
</script>
- 多事件触发
含义:一次动作执行多个函数 作用:同时做两件事
javascript
<template>
<button @click="fn1(), fn2()">多事件</button>
</template>
<script setup>
const fn1 = () => console.log('事件1')
const fn2 = () => console.log('事件2')
</script>
- 点击节流(防重复)
含义:限制频繁点击 作用:防重复提交、防重复请求
javascript
<template>
<button @click="submit" :disabled="lock">提交</button>
</template>
<script setup>
import { ref } from 'vue'
const lock = ref(false)
const submit = () => {
if (lock.value) return
lock.value = true
setTimeout(() => lock.value = false, 1000)
}
</script>
- 长按事件
含义:按住不动触发 作用:删除、多选、菜单
javascript
<template>
<view @longpress="long">长按测试</view>
</template>
<script setup>
const long = () => uni.showToast({ title: '长按触发' })
</script>
栗子:
javascript
<template>
<view class="p-4">
<button @click="add" :disabled="lock">点击计数(节流){{ count }}</button>
<view @click="parent" class="mt-4 p-2 border">
父区域
<view @click.stop="child" class="p-2 bg-gray-100">子区域(不冒泡)</view>
</view>
<input @keyup.enter="enter" placeholder="按回车提交" class="mt-4 border p-2" />
<input @focus="focus" @blur="blur" placeholder="焦点测试" class="mt-2 border p-2" />
<button @click.once="once" class="mt-4">只触发一次</button>
</view>
</template>
<script setup>
import { ref } from 'vue'
const count = ref(0)
const lock = ref(false)
const add = () => {
if (lock.value) return
lock.value = true
count.value++
setTimeout(() => lock.value = false, 800)
}
const parent = () => console.log('父触发')
const child = () => console.log('子触发')
const enter = () => uni.showToast({ title: '回车提交' })
const focus = () => console.log('聚焦')
const blur = () => console.log('失焦')
const once = () => uni.showToast({ title: '成功触发一次' })
</script>
<style>
.p-4{padding:30rpx;}
.mt-4{margin-top:30rpx;}
.mt-2{margin-top:20rpx;}
.p-2{padding:20rpx;}
.border{border:1rpx solid #eee;}
.bg-gray-100{background:#f5f5f5;}
</style>
javascript
<template>
<view class="container">
<view class="title">Demo</view>
<view class="section">
<text class="label">1. 点击计数</text>
<button
type="primary"
@click="handleAdd"
:disabled="isLocked"
>
点击 +1 ({{ count }})
</button>
</view>
<view class="section">
<text class="label">2. 事件冒泡 </text>
<view class="box-parent" @click="handleParentClick">
父容器 (点击我)
<view class="box-child" @click.stop="handleChildClick">
子容器 (不冒泡)
</view>
</view>
</view>
<view class="section">
<text class="label">3. 移动跟踪</text>
<view
class="move-area"
@mousemove="handleMouseMove"
@touchmove="handleMouseMove"
>
<view
class="dot"
:style="{ left: x + 'px', top: y + 'px' }"
></view>
</view>
<text>X: {{x}}, Y: {{y}}</text>
</view>
<view class="section">
<text class="label">4. 输入框交互</text>
<input
v-model="inputText"
class="input"
placeholder="测试焦点/失焦/改变"
@focus="handleFocus"
@blur="handleBlur"
@change="handleChange"
/>
<text class="tip">{{ focusTip }}</text>
</view>
<view class="section">
<text class="label">5. 键盘回车 (.enter)</text>
<input
class="input"
placeholder="按回车提交"
@keyup.enter="handleEnter"
/>
</view>
<view class="section">
<text class="label">6. 只触发一次 (.once)</text>
<button @click.once="handleOnce">领取奖励</button>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue'
const count = ref(0)
const isLocked = ref(false)
const handleAdd = () => {
if (isLocked.value) return
isLocked.value = true
count.value++
setTimeout(() => isLocked.value = false, 800)
}
const handleParentClick = () => uni.showToast({ title: '父容器触发', icon: 'none' })
const handleChildClick = () => uni.showToast({ title: '子容器触发', icon: 'none' })
const x = ref(150)
const y = ref(100)
const handleMouseMove = (e) => {
x.value = e.clientX || e.touches[0].clientX
y.value = e.clientY || e.touches[0].clientY
}
const inputText = ref('')
const focusTip = ref('请输入内容')
const handleFocus = () => focusTip.value = '已聚焦'
const handleBlur = () => focusTip.value = ' 已失焦'
const handleChange = () => uni.showToast({ title: '内容已修改', icon: 'none' })
const handleEnter = () => uni.showToast({ title: '回车提交成功!' })
const handleOnce = () => uni.showToast({ title: '奖励领取成功!', icon: 'success' })
</script>
<style scoped>
.container {
padding: 30rpx;
}
.title {
font-size: 36rpx;
font-weight: bold;
text-align: center;
margin-bottom: 40rpx;
}
.section {
margin-bottom: 40rpx;
}
.label {
font-size: 28rpx;
font-weight: bold;
margin-bottom: 15rpx;
display: block;
}
.box-parent {
width: 100%;
height: 200rpx;
background-color: #f5f5f5;
display: flex;
align-items: center;
justify-content: center;
border-radius: 10rpx;
}
.box-child {
width: 200rpx;
height: 100rpx;
background-color: #42b983;
color: white;
display: flex;
align-items: center;
justify-content: center;
border-radius: 10rpx;
}
.move-area {
width: 100%;
height: 300rpx;
background-color: #f9f9f9;
position: relative;
border-radius: 10rpx;
overflow: hidden;
}
.dot {
width: 40rpx;
height: 40rpx;
background-color: red;
border-radius: 50%;
position: absolute;
transform: translate(-50%, -50%);
}
.input {
border: 1rpx solid #eee;
padding: 20rpx;
border-radius: 10rpx;
font-size: 28rpx;
}
.tip {
font-size: 24rpx;
color: #666;
margin-top: 10rpx;
display: block;
}
button {
margin-top: 10rpx;
}
</style>