Vue: Event Handling
2022-03-20
0. Intro
Syntax: v-on:click="handler"
or @click="handler"
可以用于两种情况:
- inline handler: Inline JavaScript to be executed when the event is triggered,类似 native
onclick
attribute - method handler:A property name or path that points to a method defined on the component.
1. Inline Handlers - 适用于简单的情况
<button @click="count++">Add 1</button>
<p>Count is: {{ count }}</p>
data() {
return {
count: 0
}
}
2. Method Handlers - logic 复杂的情况
method handler 会自动接收到 trigger event 的 DOM Event object。
A method handler automatically receives the native DOM Event object that triggers it - in the example below, we are able to access the element dispatching the event via
event.target.tagName
.
<!-- `greet` is the name of the method defined above -->
<button @click="greet">Greet</button>
data() {
return {
name: 'Vue.js'
}
},
methods: {
greet(event) {
// `this` inside methods points to the current active instance
alert(`Hello ${this.name}!`)
// `event` is the native DOM event
if (event) {
alert(event.target.tagName)
}
}
}
3. Method vs. Inline Detection
Q:Compiler 怎么知道 @click="abc"
的 abc 是个 data property 还是个 method?
A:Compiler 会通过 check v-on
value string 是否是一个有效的 JS identifier 或者 property access path 来 detect method handler。
The template compiler detects method handlers by checking whether the v-on value string is a valid JavaScript identifier or property access path. For example,
foo
,foo.bar
andfoo['bar']
are treated as method handlers, whilefoo()
andcount++
are treated as inline handlers.
4. Calling Methods in Inline Handlers
我们除了可以 bind method name 之外,还可以 call method in inline handler,这样我们可以 pass custom arguments to the method instead of the native event.
Example
<button @click="say('hello')">Say hello</button>
<button @click="say('bye')">Say bye</button>
methods: {
say(message) {
alert(message)
}
}
5. Accessing Event Argument in Inline Handlers
假如我们需要在 inline handler 里面 access original DOM event,可以通过 $event
variable 来传入 method,或者使用 arrow function。
Example
<!-- using $event special variable -->
<button @click="warn('Form cannot be submitted yet.', $event)">Submit</button>
<!-- using inline arrow function -->
<button @click="(event) => warn('Form cannot be submitted yet.', event)">
Submit
</button>
methods: {
warn(message, event) {
// now we have access to the native event
if (event) {
event.preventDefault()
}
alert(message)
}
}
6. Event Modifiers
为什么要在 event handler 里面用 event.preventDefault()
和 event.stopPropagation()
?
JS 有两种事件传播模式,一种是 capturing/trickling(捕获/滴流),一种是 bubbling(冒泡)。
capturing 是自上而下,bubbling 是自下而上。
两种模式的区别在于,决定 DOM element 接收事件的顺序,从而决定 event handler 被触发的顺序。
- 捕获模式:最外层 element 先接收到;
- 冒泡模式:最内层 element 先接收到;
如何定义模式?
addEventListener(type, listener, useCapture)
第三个参数是 true 表明使用 capturing 模式,默认为 bubbling。
preventDefault 用来干嘛?
假如我们想要阻止当前 element 的默认特性(比如 href 会默认跳到链接里面),我们就要用 preventDefault。
stopPropagation 用来干嘛?
假如我们只想触发当前 element,不想去传播事件,就用这个。
Vue 里面如何使用这两个 function?
我们可以直接在 v-on 里面加上这个 event modifier,就不用在 handler 里面做这件事了,代码更简洁。
modifier 就是 directive postfixes denoted by a dot.
- .stop:阻止从自身开始的冒泡行为,不让当前元素的事件继续往外触发,如阻止点击 div 内部事件、触发 div 事件
- .prevent: 阻止事件本身行为,如阻止超链接的点击跳转、form 表单的点击提交
- .self:只有是自己触发的自己才会执行,如果接收到内部的冒泡事件,依然会继续进行事件传播,但自己不会执行别人传过来的事件
- .capture:改变 js 默认的事件机制,默认是冒泡,capture 功能是将冒泡改为捕获模式
- .once:将事件设置为只执行一次,如
.click.prevent.once
代表只阻止事件的默认行为一次,当第二次触发的时候事件本身的行为会执行 - .passive: 滚动事件的默认行为 (即滚动行为) 将会立即触发,而不会等待 onScroll 完成。这个
.passive
修饰符尤其能够提升移动端的性能。
Note: Order matters when using modifiers because the relevant code is generated in the same order. Therefore using @click.prevent.self
will prevent all clicks while @click.self.prevent
will only prevent clicks on the element itself.
Note2: Do not use .passive
and .prevent
together, because .passive
already indicates to the browser that you do not intend to prevent the event’s default behavior, and you will likely see a warning from the browser if you do so. 一起使用的时候 .prevent
会被忽略。
Example
<!-- the click event's propagation will be stopped -->
<a @click.stop="doThis"></a>
<!-- the submit event will no longer reload the page -->
<form @submit.prevent="onSubmit"></form>
<!-- modifiers can be chained -->
<a @click.stop.prevent="doThat"></a>
<!-- just the modifier -->
<form @submit.prevent></form>
<!-- only trigger handler if event.target is the element itself -->
<!-- i.e. not from a child element -->
<div @click.self="doThat">...</div>
The .capture
, .once
, and .passive
modifiers mirror the options of the native addEventListener
method:
<!-- use capture mode when adding the event listener -->
<!-- i.e. an event targeting an inner element is handled here before being handled by that element -->
<div @click.capture="doThis">...</div>
<!-- the click event will be triggered at most once -->
<a @click.once="doThis"></a>
<!-- the scroll event's default behavior (scrolling) will happen -->
<!-- immediately, instead of waiting for `onScroll` to complete -->
<!-- in case it contains `event.preventDefault()` -->
<div @scroll.passive="onScroll">...</div>
7. Key Modifiers
我们可以监听 keyboard 按键。
You can directly use any valid key names exposed via KeyboardEvent.key as modifiers by converting them to kebab-case.
<!-- only call `vm.submit()` when the `key` is `Enter` -->
<input @keyup.enter="submit" />
<!-- only call `vm.onPageDown()` when the `key` is `PageDown` -->
<input @keyup.page-down="onPageDown" />
常用 key
- .enter
- .tab
- .delete (captures both “Delete” and “Backspace” keys)
- .esc
- .space
- .up
- .down
- .left
- .right
我们还可以加上其他的 modifier,也就是定义组合键:
- .ctrl
- .alt
- .shift
- .meta:在 mac 里面是 command key,在 windows 里面是 windows key
Example
<!-- Alt + Enter -->
<input @keyup.alt.enter="clear" />
<!-- Ctrl + Click -->
<div @click.ctrl="doSomething">Do something</div>
.exact
modifier - 多一个不行,少一个也不行
<!-- this will fire even if Alt or Shift is also pressed -->
<button @click.ctrl="onClick">A</button>
<!-- this will only fire when Ctrl and no other keys are pressed -->
<button @click.ctrl.exact="onCtrlClick">A</button>
<!-- this will only fire when no system modifiers are pressed -->
<button @click.exact="onClick">A</button>
8. Mouse Button Modifiers
- .left
- .right
- .middle