Skip to content
On this page

事件处理

事件监听

Vue.js提供了v-on指令(简写为@)来监听DOM事件,以便在事件触发时执行JavaScript代码。

基本用法

vue
<template>
  <div>
    <button v-on:click="counter++">Add 1</button>
    <p>The button above has been clicked {{ counter }} times.</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      counter: 0
    }
  }
}
</script>

也可以使用简写形式:

vue
<template>
  <div>
    <button @click="counter++">Add 1</button>
    <p>The button above has been clicked {{ counter }} times.</p>
  </div>
</template>

方法事件处理器

对于更复杂的逻辑,我们可以将事件处理程序定义为一个方法:

vue
<template>
  <div>
    <button @click="greet">Greet</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      name: 'Vue.js'
    }
  },
  methods: {
    greet(event) {
      // `this` 会指向当前组件实例
      alert('Hello ' + this.name + '!')
      // `event` 是原生 DOM event
      if (event) {
        alert(event.target.tagName)
      }
    }
  }
}
</script>

内联处理器中的方法

除了直接绑定方法名,还可以在模板中使用内联JavaScript语句:

vue
<template>
  <div>
    <button @click="greet">Greet the user</button>
    <button @click="greetWithMessage('Hello')">Greet with message</button>
    <button @click="counter += 1">Add 1</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      counter: 0
    }
  },
  methods: {
    greet() {
      alert('Hello!')
    },
    greetWithMessage(message) {
      alert(message)
    }
  }
}
</script>
</template>

事件修饰符

Vue.js为v-on提供了事件修饰符,可以通过由点.表示的指令后缀来调用。

.stop 修饰符

.stop修饰符会调用event.stopPropagation(),阻止事件冒泡:

vue
<template>
  <div @click="outerClick">
    <button @click.stop="innerClick">Click me</button>
  </div>
</template>

<script>
export default {
  methods: {
    outerClick() {
      console.log('Outer div clicked')
    },
    innerClick() {
      console.log('Button clicked, but event won\'t bubble up')
    }
  }
}
</script>

.prevent 修饰符

.prevent修饰符会调用event.preventDefault(),阻止事件的默认行为:

vue
<template>
  <form @submit.prevent="onSubmit">
    <input type="text" placeholder="Enter text">
    <button type="submit">Submit</button>
  </form>
</template>

<script>
export default {
  methods: {
    onSubmit() {
      console.log('Form submitted, but default behavior prevented')
    }
  }
}
</script>

.capture 修饰符

.capture修饰符会在捕获阶段触发事件处理程序,而不是冒泡阶段:

vue
<template>
  <div @click.capture="outerClick">
    <button @click="innerClick">Click me</button>
  </div>
</template>

<script>
export default {
  methods: {
    outerClick() {
      console.log('Outer div clicked in capture phase')
    },
    innerClick() {
      console.log('Button clicked')
    }
  }
}
</script>

.self 修饰符

.self修饰符只会触发在元素自身上的事件,而不是从子元素冒泡来的事件:

vue
<template>
  <div @click.self="divClick" class="outer-div">
    <button @click="buttonClick">Click me</button>
  </div>
</template>

<script>
export default {
  methods: {
    divClick() {
      console.log('Div clicked, but only if clicked directly on div')
    },
    buttonClick() {
      console.log('Button clicked')
    }
  }
}
</script>

<style>
.outer-div {
  padding: 20px;
  background-color: #f0f0f0;
}
</style>

.once 修饰符

.once修饰符确保事件处理程序只触发一次:

vue
<template>
  <button @click.once="clickOnce">Click me once</button>
</template>

<script>
export default {
  methods: {
    clickOnce() {
      console.log('This will only execute once')
    }
  }
}
</script>

.passive 修饰符

.passive修饰符对应于原生DOM的passive选项,表示事件处理程序不会调用preventDefault()

vue
<template>
  <!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 -->
  <!-- 而不会等待 `onScroll` 完成 -->
  <!-- 这在处理滚动事件时可以提升性能 -->
  <div @scroll.passive="onScroll">
    <!-- 内容 -->
  </div>
</template>

<script>
export default {
  methods: {
    onScroll() {
      console.log('Scrolling...')
    }
  }
}
</script>

按键修饰符

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

常用按键修饰符

vue
<template>
  <div>
    <!-- 只有在 keyCode 是 13 时调用 vm.submit() -->
    <input @keyup.13="submit">
    
    <!-- 使用按键别名 -->
    <input @keyup.enter="submit">
    
    <!-- 使用驼峰命名 -->
    <input @keyup.page-down="onPageDown">
  </div>
</template>

<script>
export default {
  methods: {
    submit() {
      console.log('Submitted!')
    },
    onPageDown() {
      console.log('Page down key pressed')
    }
  }
}
</script>
</template>

系统修饰键

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

vue
<template>
  <div>
    <!-- Alt + Enter -->
    <input @keyup.alt.enter="clear">
    
    <!-- Ctrl + Click -->
    <div @click.ctrl="doSomething">Do something</div>
    
    <!-- Shift + Click -->
    <div @click.shift="doSomething">Do something</div>
    
    <!-- Meta (Windows键/Command键) + Click -->
    <div @click.meta="doSomething">Do something</div>
  </div>
</template>

<script>
export default {
  methods: {
    clear() {
      console.log('Cleared!')
    },
    doSomething() {
      console.log('Something done!')
    }
  }
}
</script>
</template>

exact 修饰符

.exact修饰符允许控制触发一个事件所需的精确系统修饰符组合:

vue
<template>
  <div>
    <!-- 即使 Alt 或 Shift 被一同按下时也会触发 -->
    <button @click.ctrl="onClick">A</button>
    
    <!-- 有且只有 Ctrl 被按下的时候才触发 -->
    <button @click.ctrl.exact="onCtrlClick">A</button>
    
    <!-- 没有任何系统修饰符被按下时才触发 -->
    <button @click.exact="onClick">A</button>
  </div>
</template>

<script>
export default {
  methods: {
    onClick() {
      console.log('Clicked!')
    },
    onCtrlClick() {
      console.log('Ctrl + Click!')
    }
  }
}
</script>
</template>

鼠标按钮修饰符

vue
<template>
  <div>
    <!-- 只有左键点击时触发 -->
    <div @click.left="onLeftClick">Left Click</div>
    
    <!-- 只有右键点击时触发 -->
    <div @click.right="onRightClick">Right Click</div>
    
    <!-- 只有中键点击时触发 -->
    <div @click.middle="onMiddleClick">Middle Click</div>
  </div>
</template>

<script>
export default {
  methods: {
    onLeftClick() {
      console.log('Left button clicked')
    },
    onRightClick() {
      console.log('Right button clicked')
    },
    onMiddleClick() {
      console.log('Middle button clicked')
    }
  }
}
</script>
</template>

事件处理中的参数传递

传递额外参数

vue
<template>
  <div>
    <button @click="greet('Vue.js', $event)">Greet</button>
  </div>
</template>

<script>
export default {
  methods: {
    greet(name, event) {
      // 现在我们有了访问原生事件对象
      if (event) {
        event.preventDefault()
      }
      alert('Hello ' + name + '!')
    }
  }
}
</script>
</template>

组合式API中的事件处理

在组合式API中,事件处理方式类似,但使用refreactive来管理状态:

vue
<script setup>
import { ref } from 'vue'

const counter = ref(0)
const message = ref('Hello Vue!')

function increment() {
  counter.value++
}

function updateMessage(msg, event) {
  message.value = msg
  if (event) {
    console.log(event.target.tagName)
  }
}
</script>

<template>
  <div>
    <p>{{ message }}</p>
    <p>Count: {{ counter }}</p>
    <button @click="increment">Increment</button>
    <button @click="updateMessage('Updated!', $event)">Update Message</button>
  </div>
</template>

事件处理最佳实践

1. 事件处理函数应保持简洁

vue
<template>
  <div>
    <!-- 好的做法:事件处理函数保持简洁 -->
    <button @click="handleClick">Click me</button>
  </div>
</template>

<script>
export default {
  methods: {
    handleClick() {
      // 复杂逻辑应委托给其他方法
      this.performComplexAction()
    },
    performComplexAction() {
      // 复杂逻辑在这里处理
      console.log('Performing complex action...')
    }
  }
}
</script>
</template>

2. 避免在模板中写复杂逻辑

vue
<template>
  <div>
    <!-- 避免这样做 -->
    <!-- <button @click="items.push({ id: Date.now(), name: 'New Item' }); counter++;">Add Item</button> -->
    
    <!-- 推荐这样做 -->
    <button @click="addItem">Add Item</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      items: [],
      counter: 0
    }
  },
  methods: {
    addItem() {
      this.items.push({ id: Date.now(), name: 'New Item' })
      this.counter++
    }
  }
}
</script>
</template>

3. 使用事件修饰符提高性能

vue
<template>
  <!-- 使用 .passive 修饰符提高滚动性能 -->
  <div @scroll.passive="onScroll" class="scroll-container">
    <!-- 大量内容 -->
  </div>
  
  <!-- 使用 .stop 防止不必要的事件冒泡 -->
  <div @click="handleContainerClick">
    <button @click.stop="handleButtonClick">Button</button>
  </div>
</template>

<script>
export default {
  methods: {
    onScroll() {
      // 滚动处理逻辑
    },
    handleContainerClick() {
      console.log('Container clicked')
    },
    handleButtonClick() {
      console.log('Button clicked, event stopped from bubbling')
    }
  }
}
</script>

<style>
.scroll-container {
  height: 200px;
  overflow-y: auto;
}
</style>

实际应用示例

表单提交与验证

vue
<template>
  <form @submit.prevent="handleSubmit">
    <div class="form-group">
      <label for="email">Email:</label>
      <input 
        id="email"
        v-model="email" 
        type="email" 
        @blur="validateEmail"
        :class="{ error: !emailValid && emailTouched }"
      >
      <span v-if="!emailValid && emailTouched" class="error-message">Invalid email</span>
    </div>
    
    <div class="form-group">
      <label for="password">Password:</label>
      <input 
        id="password"
        v-model="password" 
        type="password"
        @input="validatePassword"
        :class="{ error: !passwordValid }"
      >
      <span v-if="!passwordValid" class="error-message">Password too short</span>
    </div>
    
    <button type="submit" :disabled="!formValid">Submit</button>
  </form>
</template>

<script>
export default {
  data() {
    return {
      email: '',
      password: '',
      emailTouched: false,
      emailValid: true,
      passwordValid: true
    }
  },
  computed: {
    formValid() {
      return this.emailValid && this.passwordValid && this.email && this.password
    }
  },
  methods: {
    validateEmail() {
      this.emailTouched = true
      const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
      this.emailValid = emailRegex.test(this.email)
    },
    validatePassword() {
      this.passwordValid = this.password.length >= 6
    },
    handleSubmit() {
      if (this.formValid) {
        console.log('Form submitted:', { email: this.email, password: this.password })
        // 实际提交逻辑
      } else {
        console.log('Form validation failed')
      }
    }
  }
}
</script>

<style>
.form-group {
  margin-bottom: 15px;
}
.error {
  border-color: red;
}
.error-message {
  color: red;
  font-size: 12px;
  margin-top: 5px;
  display: block;
}
</style>

事件处理是Vue.js中实现用户交互的重要机制,通过合理使用事件修饰符和事件处理方法,可以构建出响应迅速、交互性强的用户界面。