vue+tsx场景下,事件修饰符的一些坑

2,814 阅读2分钟

前言

正在做右键菜单的组件,组件环境是vue3 tsx,但在使用事件修饰符withModifiers的时候遇到右键修饰符失效的情况,在这里记录下解决方法,并且通过分析源码来排查问题

什么是事件修饰符

vue提供了多个事件修饰符,可以查阅文档看详情,我们常用的@click.prevent中的.prevent就是其中一个修饰符

image.png

注意看,当使用.right的时候是可以控制鼠标右键的触发函数的,可以看以下demo

运行demo,右键红色方块,打开控制台,能看到正常输出内容,并且不会打开普通的右键菜单

TS(X)下使用事件修饰符

在TS环境下使用事件修饰符,这是官方文档的介绍,我们使用vue playground来实现

import { defineComponent, h, withModifiers } from 'vue'

export default defineComponent({
  name: 'Comp',
  setup() {
    return () => h('div', {
      style: {
      	width: '100px',
        height: '100px',
        background: 'green'
      },
      onClick: withModifiers(() => {
        alert(1)
      }, ['right', 'prevent']),
    }, 'TSX组件')
  }
})

点击这里查看效果

会发现右键绿色方块,并不会执行传入的事件,而是打开了右键菜单,这表示我们的修饰符并没有生效

修复以上右键修饰符问题

起初我以为是vue出问题了(别问,问就不是我的问题),并且提了个issue点击这里可查看issue内容

通过issue得知,想要控制右键方法,使用的是onContextmenu,而不是跟模板语法一样使用click,并且可以不用使用.right修饰符,这点跟react是一样的

import { defineComponent, h, withModifiers } from 'vue'

export default defineComponent({
  name: 'Comp',
  setup() {
    return () => h('div', {
      style: {
      	width: '100px',
        height: '100px',
        background: 'blue'
      },
      onContextmenu: withModifiers(() => {
        alert(1)
      }, ['prevent']),
    }, 'TSX组件')
  }
})

点击这里查看正确效果

image.png

右键蓝色方块,就能正确执行事件,并且不打开右键菜单

同理,TSX模式需要这样写

import { defineComponent } from 'vue'

export default defineComponent({
  name: 'TsxMode',
  setup() {
    return () => (
      <div style={{width: '120px', height: '120px', backgroundColor: 'blue'}}>TsxMode</div>
    )
  }
})

浅谈原因

通过issue得知,@click在使用.right修饰符时,会被转化成onContextmenu

转化时机在vue模板的编译转换阶段,执行transform,并且遇到v-on的时候

点击这里查看源码

export const transformOn: DirectiveTransform = (dir, node, context) => {
  return baseTransform(dir, node, context, baseResult => {
    const { modifiers } = dir
    
    // ...
    
    // normalize click.right and click.middle since they don't actually fire
    if (nonKeyModifiers.includes('right')) {
      key = transformClick(key, `onContextmenu`)
    }
    if (nonKeyModifiers.includes('middle')) {
      key = transformClick(key, `onMouseup`)
    }
    
    // ...

    return {
      props: [createObjectProperty(key, handlerExp)]
    }
  })
}

可以看到,在使用.right修饰符的时候,onClick方法会被转化成onContextmenu

同理,在使用.middle修饰符的时候,onClick方法会被转化成onMouseup

总结

vue模板语法中,可以使用@click.right.prevent来自定义鼠标右键方法

vue的ts(x)语法中,需要使用onContextmenu方法来自定义鼠标右键方法

很感谢在我issue中帮我解答问题的大兄弟 :),这里是大兄弟的github

这里可以查看三种模式的代码