vue3 使用自定义指令监听键盘事件

2,894 阅读2分钟

开始

最近有个需求,是这样的。项目键盘事件可以自定义键值。根据后台设置的键值,动态监听键盘键值。

首先看下接口返参结果:

{
  "code": 0,
  "data": [
    {
      "buttonKey": "W",
      "buttonKeyName": "W",
      "functionKey": "87",
      "isCombination": 0
    },
    {
      "buttonKey": "F",
      "buttonKeyName": "F",
      "functionKey": "70",
      "isCombination": 1
    }
  ]
}

问题

没有动态获取key值之前,我们可以这样监听:

  • 全局监听

      window.addEventListener('keydown', (event) => {
         console.log('event: ', event.key);
      });
    

这样每个页面都要做处理,如果还有组合键什么的就有点麻烦了。

  • 使用VueUseonKeyStroke

    import { onKeyStroke } from '@vueuse/core';
    onKeyStroke(['s', 'S', 'ArrowDown'], (e) => { e.preventDefault() })
    
    1. 首先是个动态获取值,如果一进来就监听, 就会出现拿不到键值对的情况。

    2. 如果监听当前页面,进入其他的页面有个相同的键值就会出现冲突。

思路

  1. 怎么使用自定义指令监听键盘事件?
  2. 怎么获取动态值?
  3. 怎么在不同页面解决冲突?

实现

如果没有用过自定义指令,可以看下用vue3自定义指令写一个按钮点击小优化 , 这个比较基础

  1. 添加自定义指令keyboard

      export const VKeyboard = {
         mounted: (el, binding) => {
           el.handler = function (event) {
             console.log(event)
           };
           // 添加监听事件
           document.addEventListener('keydown', el.handler);
         },
         updated(el) {
           document.addEventListener('keydown', el.handler);
         },
         // 绑定元素的父组件卸载后调用 
         unmounted: (el) => {
           // 删除监听事件
           document.removeEventListener('keydown', el.handler);
         },
     }
    

    组件使用

    <template>
       <div v-keyboard>延迟1s</div>
    </template>
    
  2. 怎么传值?获取最新值?

      export const VKeyboard = {
         mounted: (el, binding) => {
           el.handler = function (event) {
             console.log(event)
             const { buttonKey, isCombination = 0 } = binding.value || el.valueKeys || {};
             // 调用事件
            buttonKey === currentKey && binding.arg && binding.arg(event)
           };
           // 添加监听事件
           document.addEventListener('keydown', el.handler);
         },
         updated(el, binding) {
           // 更新获取最新键值
           el.valueKeys = binding.value;
           document.addEventListener('keydown', el.handler);
         },
         // 绑定元素的父组件卸载后调用 
         unmounted: (el) => {
           // 删除监听事件
           document.removeEventListener('keydown', el.handler);
         },
     }
    

    组件使用

    <template>
       <div v-keyboard:[keyHandle]="currentKey">延迟1s</div>
    </template>
    <script setup>
     const hotKeyList = ref([]);
     const getHotKeyList = async () => {
       const result = $api.getkeyList();
       hotKeyList.value = result;
     };
    
     const currentKey = computed(() => hotKeyList.value.find((item) => item.functionCode === 'cash_box'));
    
     const keyHandle = (event) => {
       console.log('event: ', event);
     };
    </script>
    
    
  3. 解决不同页面的冲突

    • 卸载组件删除监听事件
    • 监听元素不存在时,删除监听事件,防止调用其事件
    export const VKeyboard = {
         mounted: (el, binding) => {
           el.handler = function (event) {
             console.log(event)
             const { buttonKey, isCombination = 0 } = binding.value || el.valueKeys || {};          
             // 如果元素不存在,删除该元素监听事件,防止按键冲突
             const isHasEl = document.contains(el);
             if (!isHasEl) { 
                 document.removeEventListener('keydown', el.handler);
                 return; 
             }
            // 调用事件
            buttonKey === currentKey && binding.arg && binding.arg(event)
           };
           // 添加监听事件
           document.addEventListener('keydown', el.handler);
         },
         updated(el, binding) {
           // 更新获取最新键值
           el.valueKeys = binding.value;
           document.addEventListener('keydown', el.handler);
         },
         // 绑定元素的父组件卸载后调用 
         unmounted: (el) => {
           // 删除监听事件
           document.removeEventListener('keydown', el.handler);
         },
     }
    

最后

这只是一个解决思路,具体逻辑要看具体需求。也希望大家能提出更好的办法,共同探讨学习。

详细的可查看文档:keyboard 监听键盘

使用:

// main.js
import { createApp } from 'vue';
import { directives } from '@kvuse/components';
import App from './App.vue';

const app = createApp(App);

// 自定义指令
app.directive('keyboard', directives.keyboard);