突然发现 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 则会),这简直无力吐槽… 。不知道这个属性未来是否会做修改,感觉大概率不会。