前端Vue4.处理用户交互
墨颜丶事件的监听与处理
v-on指令(通常用@符合来代替)用来位DOM事件绑定监听
事件监听示例
关于DOM事件的绑定,简单的测试代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>事件绑定</title> <script src="https://unpkg.com/vue@3.2.47/dist/vue.global.js"></script> </head> <body> <div id="Application"> <div>点击次数:{{count}}</div> <button @click="click">点击</button> </div> <script> const App = { data(){ return { count:0 } }, methods:{ click(){ this.count += 1 } } } Vue.createApp(App).mount("#Application") </script> </body> </html>
|
单击页面中的按钮时会执行click函数,从而改变count函数的值,并可以在页面上实时看到变化效果,这是一种基础的用户交互处理方法。
当然我们也可以直接要执行的逻辑代码放入@click赋值的地方
1
| <button @click="this.count += 1">点击</button>
|
但是,通常的处理方法都不是单行的JavaScript代码可以搞定的,更多的时候时采用绑定方法函数的方式来处理事件。
定义的click
函数并没有参数,实际上当触发我们绑定的事件函数时,系统自动将当前的Event
对象传递到函数中去,如果需要使用到Event
对象,定义的处理函数如下:
1 2 3 4 5 6
| methods:{ click(event){ console.log(event) this.count += 1 } }
|
Event
对象会存储当前事件的很多信息,如事件类型,鼠标位置,键盘按键等。
如果DOM元素绑定执行事件的函数需要传入自定义的参数怎么办?例如:这个计数器的步长是可以设置的,通过函数参数来进行控制,修改click代码如下:
1 2 3
| click(step){ this.count += step }
|
在进行事件绑定时,可以采用内联处理的方式设置函数的参数
1
| <button @click="click(2)">点击</button>
|
单击页面上的按钮时,可以看到计数器以2为步长
进行增加。
如果在自定义参数的基础上,还需要使用Event对象参数,可以使用$event
来传递此参数
1
| <button @click="click(2,$event)">点击</button>
|
1 2 3 4
| click(step,event){ console.log(event) this.count += step }
|
多事件处理
多事件处理是指对于同一个用户交互事件,需要调用多个方法进行处理
。
在Vue中,绑定事件时支持使用,
对多个函数进行绑定调用
1 2 3 4 5 6 7 8
| methods:{ click(step){ this.count += step }, log(event){ console.log(event) } }
|
多事件处理时,在绑定事件都要采用内联调用的方式绑定
1
| <button @click="click(2),log($event)">点击</button>
|
事件修饰符
DOM事件的传递原理:页面触发一个单击事件时,事件首先会从父组件开始依次传递到子组件,这一过程通常被形象的成为事件捕获
,当事件传递到最上层的子组件时,其还会逆向地在进行一轮传递,从子组件依次向下传递,这一过程被称为事件冒泡
。
Vue中使用@click的方式绑定事件时,默认监听的是DOM事件的冒泡
阶段,即从子组件传递到父组件的这一过程。
1 2 3 4 5 6 7 8 9
| <div @click="click1" style="border:solid red"> 外层 <div @click="click2" style="border:solid red"> 中层 <div @click="click3" style="border:solid red"> 点击 </div> </div> </div>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| methods:{ click(step){ this.count += step }, log(event){ console.log(event) }, click1() { console.log("外层") }, click2() { console.log("中层") }, click3() { console.log("内层") } }
|
运行上面的代码,单击页面最内层的元素,通过观察控制台的打印可以看到事件函数的调用顺序
内层
中层
外层
如果要监听捕获阶段的事件
,就需要使用事件修饰符,事件修饰符capture
可以监听事件的实际设置为捕获阶段
1 2 3 4 5 6 7 8 9
| <div @click.capture="click1" style="border:solid red"> 外层 <div @click.capture="click2" style="border:solid red"> 中层 <div @click.capture="click3" style="border:solid red"> 点击 </div> </div> </div>
|
运行效果如下:
外层
中层
内层
捕获事件触发的顺序与冒泡事件相反
。实际应用中,根据具体需求来选择
理解事件的传递对处理页面用户交互至关重要,但是又很多场景不希望进行传递。
例如,当用户单击内层组件
时,我们只想让其触发内层组件的绑定方法
,当用户单击外层组件
时,只触发外层组件绑定的方法
,这就需要stop修饰符。
stop修饰符
可以阻止事件的传递
1 2 3 4 5 6 7 8 9
| <div @click.stop="click1" style="border:solid red"> 外层 <div @click.stop="click2" style="border:solid red"> 中层 <div @click.stop="click3" style="border:solid red"> 点击 </div> </div> </div>
|
此时单击时,只有被单击的组件绑定的方法才会被调用。
常用的修饰符如下:
事件修饰符 | 作用 |
---|
stop | 阻止事件传递 |
capture | 监听捕获场景的事件 |
once | 只触发一次事件 |
self | 当事件对象的target属性是当前组件时才触发事件 |
Prevent | 禁止默认事件 |
passive | 不仅指默认事件 |
注意:事件修饰符可以串联使用
,例如下面代码既能阻止事件传递,又能控制只触发一次事件。
1 2 3 4 5 6 7 8 9
| <div @click.capture="click1" style="border:solid red"> 外层 <div @click.capture="click2" style="border:solid red"> 中层 <div @click.capture.once="click3" style="border:solid red"> 点击 </div> </div> </div>
|
Vue中的事件类型
事件本身是有类型之分的,例如@click绑定的就是元素的单击事件,如果需要通过用户鼠标操作行为来实现更加复杂的交互逻辑,则需要监听更加复杂的鼠标事件,v-on指令进行普通HTML元素的事件绑定时,其支持所有原生的DOM事件,其也可以支持自定义事件。
常用事件类型
click事件是页面开发中常用的交互事件,当HTML元素被单击时会触发此事件,常用的交互事件如下:
事 件 | 意 义 | 可用的元素 |
---|
click | 单击事件,当组件被单击时触发 | 大部分HTML元素 |
dblclick | 双击事件,当组件被双击时触发 | 大部分HTML元素 |
focus | 获取焦点事件,例如输入框开启编辑模式时触发 | inout、select、textarea等 |
blur | 失去焦点事件,例如输入框结束编辑模式时触发 | inout、select、textarea等 |
change | 元素内容改变事件,输入框结束输入后,如果内容有变化,会触发此事件 | inout、select、textarea等 |
select | 元素内容选中事件,输入框中的文本被选中时会触发此事件 | inout、select、textarea等 |
mousedown | 鼠标按键被按下事件 | 大部分HTML元素 |
mouseup | 鼠标按键抬起事件 | 大部分HTML元素 |
mousemove | 鼠标在组件内移动事件 | 大部分HTML元素 |
mouseout | 鼠标移出组件时触发 | 大部分HTML元素 |
mouseover | 鼠标移入组件时触发 | 大部分HTML元素 |
Keydown | 键盘按键被按下 | HTML中所有表单元素 |
keyup | 键盘按键被抬起 | HTML中所有表单元素 |
测试代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>事件类型</title> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="Application"> <div @click="click">单击事件</div> <div @dblclick="dblclick">双击事件</div> <input @focus="focus" @blur="blur" @change="change" @select="select"> <div @mousedown="mousedown">鼠标按下</div> <div @mouseup="mouseup">鼠标抬起</div> <div @mousemove="mousemove">鼠标移动</div> <div @mouseout="mouseout" mouseover="mouseover">鼠标移入移出</div> <input @keydown="keydown" @keyup="keyup"> </div> <script> const App = { methods:{ click(){ console.log("单击事件") }, dblclick(){ console.log("双击事件") }, focus(){ console.log("获取焦点") }, blur(){ console.log("失去焦点") }, change(){ console.log("内容改变") }, select(){ console.log("文本选中") }, mousedown(){ console.log("鼠标按键按下") }, mouseup(){ console.log("鼠标按键抬起") }, mousemove(){ console.log("鼠标移动") }, mouseout(){ console.log("鼠标移出") }, mouseover(){ console.log("鼠标移入") }, keydown(){ console.log("键盘按键按下") }, keyup(){ console.log("键盘按键抬起") }, } } Vue.createApp(App).mount("#Application") </script> </body> </html>
|
对于每一种类型的事件,都可以通过Event对象来获取事件的具体信息,例如在鼠标单击事件中,可以获取用户单击的是左键还是右键
按键修饰符
对键盘按键进行监听时,通过使用keyup参数,如果仅仅对某个按键进行监听,可以通过Event对象来判断,例如监听用户敲击回车键。注意大小写
1 2 3 4 5 6
| keyup(event){ console.log("键盘按键抬起") if (event.key == 'Enter'){ console.log('回车键按下') } },
|
还有一种更简单的方法实现
1
| <input @keyup.enter="keyup">
|
注意:修饰符命名规则与Event对象中属性key值的命名规则不同
Event对象中的属性采用的是大写字母驼峰法:Enter,PageDown
按键修饰符采用的是小写中划线:enter,page-down
Vue还提供了一些特殊的系统按键修饰符,配合其他键盘或鼠标按键进行的。主要有以下4种
ctrl
alt
shift
meta
这些系统修饰符使用的意义只有当用户按下这些键时,对应的键盘或鼠标才会触发,处理组合键经常会用到
1 2
| <div @mousedown.ctrl="mousedown">鼠标按下</div>
|
1 2
| <input @keyup.alt.enter="keyup">
|
1 2 3 4 5
|
<div @mousedown.ctrl.exact="mousedown">鼠标按下</div>
|
Vue中还有3个常用地鼠标按键修饰符
left 左键
right 右键
middle 滚轮
1 2
| <div @click.left="click">单击事件</div>
|
范例1:随鼠标移动地小球
X坐标 | Y坐标 | 意 义 |
---|
clientX | clientY | 鼠标位置相对当前body容器可视区域的横纵坐标 |
pageX | pageY | 鼠标位置相对整个页面的横纵坐标 |
screenX | screenY | 鼠标位置相对设备屏幕的横纵坐标 |
offsetX | offsetY | 鼠标位置相对父容器的横纵坐标 |
x | y | 与screenX和screenY意义一样 |
要实现页面元素随鼠标移动很简单,我们只需要监听鼠标移动事件,做好元素坐标的更新即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>圆球游戏</title> <script src="https://unpkg.com/vue@next"></script> <style> body { margin: 0; padding: 0; } .container { margin: 0; padding: 0; position: absolute; width: 440px; height: 440px; background-color: blanchedalmond; display: inline; } .ball { position:absolute; width: 60px; height: 60px; left:100px; top:100px; background-color: red; border-radius: 30px; z-index:100 } </style> </head> <body> <div id="Application"> <div class="container" @mousemove.stop="move"> <div class="ball" :style="{left: offsetX+'px', top:offsetY+'px'}"> </div> </div> </div> <script> const App = { data() { return { offsetX:0, offsetY:0 } }, methods: { move(event) { if (event.clientX + 30 > 440) { this.offsetX = 440 - 60 } else if (event.clientX - 30 < 0) { this.offsetX = 0 } else { this.offsetX = event.clientX - 30 } if (event.clientY + 30 > 440) { this.offsetY = 440 - 60 } else if (event.clientY - 30 < 0) { this.offsetY = 0 } else { this.offsetY = event.clientY - 30 } } } } Vue.createApp(App).mount("#Application") </script> </body> </html>
|
范例2:弹球游戏
1 2 3 4 5 6 7 8 9 10 11
| <div id="Application"> <div class="container"> <div class="board" :style="{left: boardX + 'px'}"></div> <div class="ball" :style="{left: ballX+'px', top: ballY+'px'}"></div> <h1 v-if="fail" style="text-align: center;">游戏失败</h1> </div> </div>
|
如上所示,底部挡板元素可以通过键盘来控制移动,游戏失败的提示文案默认是隐藏的,当游戏失败后控制器展示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| <style> body { margin: 0; padding: 0; } .container { position: relative; margin: 0 auto; width: 440px; height: 440px; background-color: blanchedalmond; } .ball { position:absolute; width: 30px; height: 30px; left:0px; top:0px; background-color:orange; border-radius: 30px; } .board { position:absolute; left: 0; bottom: 0; height: 10px; width: 80px; border-radius: 5px; background-color: red; } </style>
|
在控制页面布局时,当父容器position属性设置为relative时,子组件的position属性设置为absolute,可以将子组件相对应父组件进行绝对布局。实现此游戏的JavaScripts逻辑并不复杂
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
| <script> const App = { data() { return { boardX:0, ballX:0, ballY:0, rateX:0.1, rateY:0.1, fail:false } }, mounted() { this.enterKeyup(); this.rateX = (Math.random() + 0.1) this.rateY = (Math.random() + 0.1) this.timer = setInterval(()=>{ if (this.ballX + this.rateX >= 440 - 30) { this.rateX *= -1 } if (this.ballX + this.rateX <= 0) { this.rateX *= -1 } if (this.ballY + this.rateY <= 0) { this.rateY *= -1 } this.ballX += this.rateX this.ballY += this.rateY if (this.ballY >= 440 - 30 - 10) { if (this.boardX <= this.ballX + 30 && this.boardX + 80 >= this.ballX) { this.rateY *= -1 } else { clearInterval(this.timer) this.fail = true } } },2) }, methods: { keydown(event){ if (event.key == "ArrowLeft") { if (this.boardX > 10) { this.boardX -= 20 } } else if (event.key == "ArrowRight") { if (this.boardX < 440 - 80) { this.boardX += 20 } } }, enterKeyup() { document.addEventListener("keydown", this.keydown); } } } Vue.createApp(App).mount("#Application") </script>
|
小节练习
问:Vue中绑定监听事件的指令是什么。
提示:v-on用法,熟悉使用@缩写模式
问:事件修饰符
提示:stop,prevent,capture,once等
问:如何处理组合键
提示:Vue提供了4个常用的系统修饰键,其可以与其他按键修饰符组合用,从而实现组合键监听