按键修饰符

键盘交互是提升用户体验的重要一环。用户按回车提交表单、按 ESC 关闭弹窗、按上下键选择列表项...这些操作都需要监听特定的键盘按键。

Vue 提供了按键修饰符,让键盘事件监听变得简单直观。不需要在事件处理函数中判断 event.keyCode,直接在模板中声明要监听的按键即可。

基本用法

传统方式监听回车键:

methods: {
  handleKeyup: function(event) {
    if (event.keyCode === 13) {
      // 回车键被按下
      this.submit()
    }
  }
}

使用按键修饰符:

<input @keyup.enter="submit">

简洁明了,一眼就能看出监听的是回车键。

内置按键别名

Vue 为常用按键提供了别名:

修饰符按键keyCode
.enter回车键13
.tabTab 键9
.escESC 键27
.space空格键32
.up上箭头38
.down下箭头40
.left左箭头37
.right右箭头39
.delete删除键(包括 Backspace 和 Delete)8 / 46

使用示例

<div id="app">
  <!-- 回车提交 -->
  <input 
    type="text" 
    v-model="message" 
    @keyup.enter="sendMessage"
    placeholder="按回车发送"
  >
  
  <!-- ESC 清空 -->
  <input 
    type="text" 
    v-model="search" 
    @keyup.esc="clearSearch"
    placeholder="按 ESC 清空"
  >
  
  <!-- 方向键导航 -->
  <div 
    @keyup.up="selectPrev" 
    @keyup.down="selectNext"
    tabindex="0"
  >
    使用上下键选择
  </div>
  
  <!-- 空格播放/暂停 -->
  <div @keyup.space="togglePlay" tabindex="0">
    按空格播放/暂停
  </div>
</div>

<script>
new Vue({
  el: '#app',
  data: {
    message: '',
    search: ''
  },
  methods: {
    sendMessage: function() {
      if (this.message.trim()) {
        console.log('发送消息:', this.message)
        this.message = ''
      }
    },
    clearSearch: function() {
      this.search = ''
    },
    selectPrev: function() {
      console.log('选择上一个')
    },
    selectNext: function() {
      console.log('选择下一个')
    },
    togglePlay: function() {
      console.log('播放/暂停')
    }
  }
})
</script>

使用 keyCode

如果需要监听没有别名的按键,可以直接使用 keyCode:

<!-- 监听 F1 键(keyCode 112) -->
<input @keyup.112="showHelp">

<!-- 监听数字键 1(keyCode 49) -->
<input @keyup.49="selectFirst">

注意

直接使用 keyCode 已被 Web 标准废弃,不同键盘布局可能产生不同的 keyCode。推荐使用按键别名或自定义别名。

自定义按键别名

Vue 允许你为按键定义自定义别名:

// 全局定义按键别名
Vue.config.keyCodes = {
  v: 86,
  f1: 112,
  mediaPlayPause: 179,
  up: [38, 87]  // 可以是数组,支持多个 keyCode
}

使用自定义别名:

<input @keyup.media-play-pause="togglePlay">
<input @keyup.f1="showHelp">

系统修饰键

Vue 提供了系统修饰键,可以与其他按键组合使用:

修饰符按键说明
.ctrlCtrl 键
.altAlt 键
.shiftShift 键
.metaMeta 键Mac 上是 ⌘,Windows 上是 ⊞

组合键监听

<div id="app">
  <!-- Ctrl + S 保存 -->
  <input @keyup.ctrl.s="save" placeholder="Ctrl+S 保存">
  
  <!-- Ctrl + Enter 发送 -->
  <textarea @keyup.ctrl.enter="send" placeholder="Ctrl+Enter 发送"></textarea>
  
  <!-- Alt + N 新建 -->
  <div @keyup.alt.n="createNew" tabindex="0">Alt+N 新建</div>
  
  <!-- Shift + Enter 换行 -->
  <textarea @keyup.shift.enter="newLine" placeholder="Shift+Enter 换行"></textarea>
  
  <!-- Ctrl + Shift + S 另存为 -->
  <input @keyup.ctrl.shift.s="saveAs" placeholder="Ctrl+Shift+S 另存为">
</div>

系统键的精确控制

默认情况下,系统修饰键可以单独触发。比如 @keyup.ctrl 会在单独按下 Ctrl 时触发。

使用 .exact 修饰符可以精确控制组合键:

<!-- 只有 Ctrl 被按下时触发(不能同时按其他修饰键) -->
<button @click.ctrl.exact="handleCtrlClick">Ctrl + 点击</button>

<!-- 只有 Ctrl + Shift 被按下时触发 -->
<button @click.ctrl.shift.exact="handleCtrlShiftClick">Ctrl + Shift + 点击</button>

<!-- 没有任何修饰键时触发 -->
<button @click.exact="handleClick">普通点击</button>

实战示例

搜索框回车搜索

<div class="search-box">
  <input 
    type="text" 
    v-model="keyword"
    @keyup.enter="search"
    @keyup.esc="clearKeyword"
    placeholder="输入关键词,回车搜索"
  >
  <button @click="search">搜索</button>
</div>

<script>
methods: {
  search: function() {
    if (!this.keyword.trim()) return
    console.log('搜索:', this.keyword)
    // 调用搜索 API
  },
  clearKeyword: function() {
    this.keyword = ''
  }
}
</script>

快捷键系统

<div 
  class="app" 
  @keyup.ctrl.s="save"
  @keyup.ctrl.z="undo"
  @keyup.ctrl.y="redo"
  @keyup.ctrl.shift.s="saveAs"
  @keyup.esc="closeModal"
  tabindex="0"
>
  <div class="editor">
    <textarea v-model="content"></textarea>
  </div>
  
  <div class="modal" v-if="showModal">
    <p>弹窗内容</p>
    <button @click="closeModal">关闭(ESC)</button>
  </div>
</div>

<script>
new Vue({
  el: '.app',
  data: {
    content: '',
    showModal: false
  },
  methods: {
    save: function() {
      console.log('保存内容')
    },
    saveAs: function() {
      console.log('另存为')
    },
    undo: function() {
      console.log('撤销')
    },
    redo: function() {
      console.log('重做')
    },
    closeModal: function() {
      this.showModal = false
    }
  }
})
</script>

列表键盘导航

<div class="list-container" tabindex="0" @keyup="handleKeyNav">
  <div 
    v-for="(item, index) in items" 
    :key="item.id"
    :class="{ active: index === selectedIndex }"
    @click="selectedIndex = index"
  >
    {{ item.name }}
  </div>
</div>

<script>
new Vue({
  data: {
    items: [
      { id: 1, name: '选项一' },
      { id: 2, name: '选项二' },
      { id: 3, name: '选项三' },
      { id: 4, name: '选项四' }
    ],
    selectedIndex: 0
  },
  methods: {
    handleKeyNav: function(event) {
      switch(event.key) {
        case 'ArrowUp':
        case 'Up':
          if (this.selectedIndex > 0) {
            this.selectedIndex--
          }
          break
        case 'ArrowDown':
        case 'Down':
          if (this.selectedIndex < this.items.length - 1) {
            this.selectedIndex++
          }
          break
        case 'Enter':
          this.selectItem(this.items[this.selectedIndex])
          break
      }
    },
    selectItem: function(item) {
      console.log('选择了:', item.name)
    }
  }
})
</script>

游戏控制

<div 
  class="game-area" 
  tabindex="0"
  @keyup.up="moveUp"
  @keyup.down="moveDown"
  @keyup.left="moveLeft"
  @keyup.right="moveRight"
  @keyup.space="jump"
>
  <div class="player" :style="playerStyle"></div>
</div>

<script>
new Vue({
  data: {
    position: { x: 0, y: 0 }
  },
  computed: {
    playerStyle: function() {
      return {
        transform: `translate(${this.position.x}px, ${this.position.y}px)`
      }
    }
  },
  methods: {
    moveUp: function() {
      this.position.y -= 10
    },
    moveDown: function() {
      this.position.y += 10
    },
    moveLeft: function() {
      this.position.x -= 10
    },
    moveRight: function() {
      this.position.x += 10
    },
    jump: function() {
      console.log('跳跃!')
    }
  }
})
</script>

注意事项

tabindex 属性

非表单元素(如 div)默认无法获得焦点,需要添加 tabindex 属性才能响应键盘事件:

<div tabindex="0" @keyup.enter="handleEnter">
  这个 div 可以获得焦点
</div>

按键冲突

避免与浏览器快捷键冲突。比如 Ctrl+S 默认会触发浏览器保存,需要阻止默认行为:

<div @keydown.ctrl.s.prevent="save">
  Ctrl+S 保存(阻止浏览器默认行为)
</div>

小结

按键修饰符让键盘事件监听变得简单:

  1. 内置别名.enter.esc.space 等常用按键
  2. 系统修饰键.ctrl.alt.shift.meta 组合使用
  3. 精确控制.exact 确保只有特定组合键触发
  4. 自定义别名Vue.config.keyCodes 定义按键别名

合理使用按键修饰符,可以大幅提升应用的交互体验。记住要考虑无障碍访问,为键盘用户提供良好的操作体验。