巨坑的事件绑定

107 阅读2分钟

突然发现 vue3 的一个很神奇的地方,具体看代码:

export const App = () => {
  return (
    <div style={{ color:red” }}>
      <Button />
      <CountValue
        countValue={count.value}
        onClick={() => {
          *console*.log(“click”)
        }}>
        你好
      </CountValue>
    </div>
  )
}

function CountValue(
  { countValue, onClick }: { countValue: number; onClick: () => void },
  { slots }: { slots: any },
) {
  return (
    <div>
      <button
        onClick={() => {
          onClick1()
        }}>
        按钮
      </button>
      {slots.default()}
      {countValue}
    </div>
  )
}

那么你认为当我点击按钮的时候,会触发 onClick 事件几次?一次吧?
并不!
他会触发两次!!!

你以为到这就结束了?
并不!!!
不但点击按钮会触发,点击子组件(CountValue 组件)任何一个地方他都会触发!!! 我一度怀疑是不是我的代码出了 bug,反复检查发现,只要是绑定的和原生事件同名的事件,它都会在根节点上自动绑定!!! 这简直是一个神坑操作,我查阅了一下,目前网上提到的人并不多,但是普遍认为这是一个很坑的操作。我也是这个时候才明白了 defineComponent 根本不是给 vue 模板用的,是 vue 自己造出来为了让你声明事件用的,只有你在 defineComponent 里明确声明的事件他才不会绑定,否则他都会强制绑定到根节点去。参考这个代码,是没有问题的

const CountValue = defineComponent({
  props: {
    onClick: {
      type: Function,
    },
    countValue: Number,
  },
  setup(props) {
    return () => (
      <div>
        <button
          onClick={() => {
            if (props.onClick) {
              props.onClick()
            }
          }}>
          按钮
        </button>
        {props.countValue}
      </div>
    )
  },
})

必须要强制声明 props,他才不会强制绑定到根节点。 我真的无法理解这是个什么设计,既然有了 Typescript,有了 props 这种 js 原生支持的传递方式,你为什么非要抱着自己搞出来的这套不放呢?

{onClick, countValue}: {onClick: () => void; countValue: number}

就目前而言除了放弃 on 开头 改用 bindClick 这种之外,我没有发现什么好的规避这个设定的方法。

补充:刚刚仔细翻看了文档,发现 vue 官方给了一个 inheritAttrts 可以控制是否绑定,看了下这个 api,是从 vue2.4 加入的,当时我已经不用 vue 了难怪不知道,但是这个正如我之前说的,必须要套一层 defineComponent 才能声明,而且还要每次强制手动声明,同时对于高阶组件依旧没什么用。另外这个属性在 vue2 和 vue3 甚至会出现表现不一致的情况(2 这个无论怎么设置不会影响 class 和 style,而在 3 则会),这简直无力吐槽… 。不知道这个属性未来是否会做修改,感觉大概率不会。