1. 前言

本小节我们介绍 Vue 中如何进行事件处理。在章节 2.2 中我们已经介绍了指令 v-on,本章节我们将详细介绍在 v-on 的一些用法。包括如何传递参数、如何使用事件修饰符等。其中,事件修饰符是本章节的难点。事件修饰符很多,同学们不需要一下子都记住,只要学会如何使用它,在开发过程中如果有所遗忘,可以通过翻看文档来使用。

2. 慕课解释

可以用 v-on 指令监听 DOM 事件,并在触发时运行一些 JavaScript 代码。 — 官方定义

使用 v-on 指令监听 DOM 事件,并在触发时运行一些 JavaScript 代码。

3. 基本使用

3.1 监听点击事件,并在触发时运行一些 JavaScript 代码

实例演示
预览 复制
复制成功!
<!DOCTYPE html>
  <html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
  </head>
  <body>
    <div id="app">
      <div>商品数量 {{count}}</div>
      <button v-on:click="count = count + 1"> 加一个 </button>
    </div>
  </body>
  <script src="https://unpkg.com/vue/dist/vue.js"></script>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        count: 0
      },
    })
  </script>
</html>

运行案例 点击 "运行案例" 可查看在线运行效果

代码解释:
在 html 第 3 行,我们使用 v-on 指令给 button 元素绑定来一个 click 事件,并在点击的时候执行 count + 1 的操作。

3.2 通过 methods 实现事件函数绑定

在上面的例子中,我们把事件处理函数直接写在模板中,然而许多事件处理的逻辑都很复杂,所以直接把JS代码写在 v-on 指令中有时并不可行,v-on 指令可以接收一个定义的方法来调用。示例如下:

实例演示
预览 复制
复制成功!
<!DOCTYPE html>
  <html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
  </head>
  <body>
    <div id="app">
      <div>商品数量 {{count}}</div>
      <button v-on:click="add"> 加一个 </button>
    </div>
  </body>
  <script src="https://unpkg.com/vue/dist/vue.js"></script>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        count: 0
      },
      methods: {
        add() {
          this.count = this.count + 1
        }
      }
    })
  </script>
</html>

运行案例 点击 "运行案例" 可查看在线运行效果

代码解释:
在 html 第 3 行,我们使用 v-on 指令给 button 元素绑定来一个 click 事件,并在点击的时候执行 add 方法。
在 JS 代码第 9-11 行,我们定义了方法 add;触发该方法时给 count + 1。

3.3 给事件传递参数

有时候我们需要在事件触发的时候传递一些参数,

实例演示
预览 复制
复制成功!
<!DOCTYPE html>
  <html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
  </head>
  <body>
    <div id="app">
      <div>商品数量 {{count}}</div>
      <button v-on:click="setCount(10)"> 设置为 10 个 </button>
      <button v-on:click="setCount(0)"> 设置为 0 个 </button>
    </div>
  </body>
  <script src="https://unpkg.com/vue/dist/vue.js"></script>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        count: 0
      },
      methods: {
        setCount(count, event) {
          this.count = count
        }
      }
    })
  </script>
</html>
运行案例 点击 "运行案例" 可查看在线运行效果

代码解释:
在 html 第 3 行,我们使用 v-on 指令给 button 元素绑定了一个 click 事件,并在点击的时候执行 add 方法。
在 JS 代码第 9-11 行,我们定义了方法 add;触发该方法时给 count + 1。

3.4 获取原生 DOM 事件

有时我们需要在内联语句处理器中访问原始的 DOM 事件。可以用特殊变量 $event 把它传入方法:

实例演示
预览 复制
复制成功!
<!DOCTYPE html>
  <html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
  </head>
  <body>
    <div id="app">
      <div>商品数量 {{count}}</div>
      <button v-on:click="setCount(10, $event)"> 设置为 10 个 </button>
      <button v-on:click="setCount(0, $event)"> 设置为 0 个 </button>
    </div>
  </body>
  <script src="https://unpkg.com/vue/dist/vue.js"></script>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        count: 0
      },
      methods: {
        setCount(count, event) {
          this.count = count
          console.log(event)
        }
      }
    })
  </script>
</html>

运行案例 点击 "运行案例" 可查看在线运行效果

代码解释:
在上述 JS 代码第 9-12 行,我们定义了事件函数 setCount。 在按钮点击事件中,我们将 $event 对象传递给函数,因此,在函数 setCount 中可以访问到原生事件对象。

4. 事件修饰符

在事件处理程序中调用 event.preventDefault() 或 event.stopPropagation() 是非常常见的需求。尽管我们可以在方法中轻松实现这点,但更好的方式是:方法只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。为了解决这个问题,Vue.js 为 v-on 提供了事件修饰符。实现方法就是在事件名称后面以后缀的形式添加指定的修饰符。

知识扩展:
event.preventDefault() 用来取消事件的默认动作。
event.stopPropagation() 用来阻止事件冒泡到父元素,阻止任何父事件处理程序被执行。

Vue 提供了以下事件修饰符:

  1. .stop: 阻止单击事件继续传播;
  2. .prevent: 只有修饰符,提交事件不再重载页面;
  3. .capture: 添加事件监听器时使用事件捕获模式,即元素自身触发的事件先在自身处理,然后交由内部元素进行处理;
  4. .self: 只有在event.target是当前元素自身时触发处理函数,即事件不是从内部元素触发的;
  5. .once: 点击事件将只触发一次;
  6. .passive: 滚动事件会立即触发,不会等待其他串联事件。即prevent会失效。
<!-- 阻止单击事件继续传播 -->
<a v-on:click.stop="doThis"></a>

<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>

<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>

<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->
<div v-on:click.capture="doThis">...</div>

<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>

<!-- 点击事件将只会触发一次 -->
<a v-on:click.once="doThis"></a>

接下来,我们用一个完整的示例来看下这些修饰符的使用方法。

实例演示
预览 复制
复制成功!
<!DOCTYPE html>
  <html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
  </head>
  <style>
    .container{
      height: 400px;
      overflow-x: auto;
      flex: 1;
    }
    .container div{
      height: 20px;
    }
  </style>
  <body>
    <div id="app">
      <!-- stop 案例 -->
      <div @click="clickTargetContainer">
        <button @click.stop="clickTargetA">点我 stop 案例</button>
        <button @click="clickTargetA">点我 无 stop 修饰</button>
      </div>

      <!-- submit.prevent 案例 -->
      <form method="get" action="/" @submit.prevent="submit">
        <button type="submit">点我提交表单</button>
      </form>

      <form method="get" action="https://www.baidu.com" @submit="submit">
        <button type="submit">点我提交表单</button>
      </form>

      <!-- capture 案例 -->
      <div v-on:click.capture="capture">
        <button @click.stop="clickTargetA">点我 capture 案例</button>
      </div>

      <!-- self 案例 -->
      <div @click.self="clickTargetContainer" style="padding: 20px;border: 1px solid #cccccc;">
        <button @click="clickTargetA">点我 self 案例</button>
      </div>

      <!-- once 案例 -->
      <div>
        <button @click.once="clickTargetA">点我 once 案例</button>
      </div>

    </div>
  </body>
  <script src="https://unpkg.com/vue/dist/vue.js"></script>
  <script>
    var vm = new Vue({
      el: '#app',
      methods: {
        clickTargetContainer() {
          alert('父级容器点击事件触发')
        },
        clickTargetA() {
          alert('按钮标签点击事件触发')
        },
        submit() {
          alert('提交表单了')
        },
        capture() {
          alert('虽然你点击的是内部元素,但是我先触发!')
        }
      }
    })
  </script>
</html>

运行案例 点击 "运行案例" 可查看在线运行效果

代码解释:
代码第 4-7 行,stop 案例中,当我们给按钮 click 事件添加 .stop 修饰符之后,点击按钮,事件不会向上传递。
代码第 10-16 行,submit.prevent 案例中,当给 submit 事件添加 .prevent 修饰符之后,提交事件不再重载页面。
代码第 19-21 行,capture 案例中,我们给父容器添加了 capture 事件,当点击按钮的时候,会先触发 capture 中的事件函数,然后再触发按钮绑定的点击事件。
代码第 24-26 行,self 案例中,我们给父容器的点击事件添加了 .self 的修饰符,所以只有点击父容器的时候才会触发该方法,当点击按钮的时候并不会触发父容器绑定的事件。
代码第 29-31 行,once 案例中,我们给按钮的点击事件添加了 .once 的修饰符,所以只有首次点击按钮的时候会触发事件函数,再次点击之后将不会触发事件函数。

5. 按键修饰符

在监听键盘事件时,我们经常需要检查详细的按键。Vue 允许为 v-on 在监听键盘事件时添加按键修饰符:

<!-- 只有在 `key` 是 `Enter` 时调用 `login()` -->
<input v-on:keyup.enter="login">

<!-- 也可以使用 keyCode -->
<input v-on:keyup.13="login">

为了在必要的情况下支持旧浏览器,Vue 提供了绝大多数常用的按键码的别名:

  1. .enter: 回车键;
  2. .tab: TAB键;
  3. .delete: 删除和退格键;
  4. .esc: 只有在event.终止键;
  5. .space: 删除键;
  6. .up: 上方向键:
  7. .down: 下方向键:
  8. .left: 左方向键:
  9. .right: 右方向键:

6. 系统修饰键

可以用如下修饰符来实现仅在按下相应按键时才触发鼠标或键盘事件的监听器。

  1. .ctrl:
  2. .alt:
  3. .shift:
  4. .meta:

注意:在 Mac 系统键盘上,meta 对应 command 键 (⌘)。在 Windows 系统键盘 meta 对应 Windows 徽标键 (⊞)。在 Sun 操作系统键盘上,meta 对应实心宝石键 (◆)。在其他特定键盘上,尤其在 MIT 和 Lisp 机器的键盘、以及其后继产品,比如 Knight 键盘、space-cadet 键盘,meta 被标记为“META”。在 Symbolics 键盘上,meta 被标记为“META”或者“Meta”。

例如:

<!-- Alt + C -->
<input @keyup.alt.67="clear">

<!-- Ctrl + Click -->
<div @click.ctrl="doSomething">Do something</div>

Vue提供了.exact修饰符,实现单独的系统按键的事件。

例如:

<!-- 即使 Alt 或 Shift 被一同按下时也会触发 -->
<button @click.ctrl="onClick">A</button>

<!-- 有且只有 Ctrl 被按下的时候才触发 -->
<button @click.ctrl.exact="onCtrlClick">A</button>

<!-- 没有任何系统修饰符被按下的时候才触发 -->
<button @click.exact="onClick">A</button>

7. 小结

本节,我们带大家学习了事件处理。主要知识点有以下几点:

  • 如何定义事件函数,如何给事件函数传递参数;
  • 通过特殊参数 $event 获取原生 DOM 事件;
  • 事件修饰符的使用;
  • 按键修饰符的使用;
  • 系统修饰键的使用。