Vue中如何通过$ref绑定mouseover事件替代模板内事件定义
解决方案
原有错误原因
- 方案1报错:你的
ref="myStats"绑定在父组件外层div元素上,并非绑定到myStats子组件实例,自然无法调用子组件的onMouseEvent方法;即使ref绑对了子组件,没有做TypeScript类型断言也会触发类型不存在的报错。 - 方案2报错:原生鼠标事件回调的
event参数是MouseEvent对象,不存在自定义的onMouseEvent方法,调用必然报错。 - 方案3逻辑本身是可行的,你只需要把
mouseOver变量的赋值和鼠标事件触发关联起来即可,不需要和子组件的ref做关联,mouseOver是父组件的响应式变量,直接控制子组件v-if即可实现显隐。
实现思路
你的需求是优先在父组件Components内部控制悬停逻辑,完全不需要调用子组件的任何方法,实现方式非常简单:
通过ref拿到父组件根DOM元素,在mounted阶段手动绑定mouseenter/mouseleave事件(比mouseover/mouseout性能更好,不会因为子元素冒泡重复触发),事件回调中直接修改mouseOver的值即可,完全不需要在模板中写事件绑定。
修正后完整代码
Components 组件代码
<template> <!-- 重命名ref为componentRoot,避免和子组件myStats重名混淆 --> <div class="pv-lookup" ref="componentRoot"> <div> <someComponent1 /> <someComponent2 /> <myStats v-if="mouseOver" @mouseMoved="onMouseMoved"/> </div> </div> </template> <script> export default class Components extends Vue { public mouseOver = false; @Emit() public onMouseMoved() { // 如果你不需要子组件的鼠标移动触发显隐,可以删除这里的赋值逻辑 this.mouseOver = !this.mouseOver; } mounted() { this.$nextTick(() => { if (this.isInhouseClient || this.isInhouse) { // 类型断言,避免TS类型报错 const rootEl = this.$refs.componentRoot as HTMLElement; // 绑定鼠标进出事件 rootEl.addEventListener('mouseenter', this.handleMouseEnter); rootEl.addEventListener('mouseleave', this.handleMouseLeave); } }); } // 组件销毁前移除事件监听,避免内存泄漏 beforeDestroy() { const rootEl = this.$refs.componentRoot as HTMLElement; rootEl?.removeEventListener('mouseenter', this.handleMouseEnter); rootEl?.removeEventListener('mouseleave', this.handleMouseLeave); } // 抽离事件回调,方便后续移除 private handleMouseEnter() { this.mouseOver = true; } private handleMouseLeave() { this.mouseOver = false; } } </script>
myStats 组件代码
无需做任何修改,保持原有代码即可。
疑问解答
- 如何通过
ref实现模板中的鼠标悬停逻辑?
通过ref获取到需要监听的DOM元素,在JS中手动绑定鼠标事件,回调中修改响应式变量控制显隐,完全可以替代模板中的@mouseover/@mouseout绑定。 - 是否必须调用
myStats的onMouseEvent方法?
完全不需要,父组件自行控制mouseOver响应式变量即可实现需求,逻辑更简洁,符合你的预期。
内容的提问来源于stack exchange,提问作者curious_birdie
相关产品推荐
相关产品推荐

