Vue基础教程(118)组件和组合API之子组件向父组件传递数据之将原生事件绑定到组件:Vue组件通信黑科技:这样“偷懒”传事件,代码直接瘦一半!

一、前言:为什么我扔掉$emit后效率翻倍?

各位前端摸鱼大师,今天咱们聊个实战中高频掉坑的场景——子组件向父组件传数据。传统教学总让你背八股文:“子组件用$emit发事件,父组件用v-on接事件”……停!这种套路写多了是不是感觉在重复造轮子?尤其是遇到输入框、按钮这类自带原生事件的元素时,每次都要手动包装成自定义事件,代码写得比相亲简历还啰嗦。

但真相是:Vue早就给你开了后门!把原生事件直接绑定到组件标签上,能让子组件秒变“事件传送门”。搭配组合API食用,更是香到离谱。接下来,我将用程序员黑话+人间清醒式演示,带你掌握这套“偷心”(偷事件)技巧。


二、基础回顾:子组件传数据的“祖传手艺”

先快速过一遍经典打法。假设有个按钮子组件,点击后要向父组件传递消息:

子组件 Child.vue

<template>
  <button @click="sendMessage">点我传信</button>
</template>
<script setup>
const emit = defineEmits(['messageSent'])

const sendMessage = () => {
  emit('messageSent', '来自子组件的密电码!')
}
</script>

父组件 Parent.vue

<template>
  <Child @message-sent="handleMessage" />
</template>
<script setup>
import Child from './Child.vue'

const handleMessage = (msg) => {
  console.log(msg) // 输出:来自子组件的密电码!
}
</script>

这套操作稳如老狗,但问题来了——如果子组件里是个原生input框,难道每次敲回车都要手动emit?太卷了吧!


三、开挂技能:让组件“裸奔”接收原生事件

核心思路:在组件标签上直接写原生事件(如@click),子组件内部用v-bind="$attrs"继承事件绑定。相当于给组件穿了件“隐形事件斗篷”。

3.1 实战演示:一键实现回车提交

子组件 SubmitButton.vue

<template>
  <!-- 用v-bind接管所有父组件传来的属性 -->
  <button v-bind="$attrs">点我</button>
</template>
<script setup>
// 无需定义emit!躺平就完事了
</script>

父组件 Parent.vue

<template>
  <!-- 直接绑定原生事件! -->
  <SubmitButton @click="handleClick" />
</template>
<script setup>
import SubmitButton from './SubmitButton.vue'

const handleClick = () => {
  console.log('按钮被戳了!')
}
</script>

原理拆解

  • $attrs是Vue的内置对象,专门接收父组件传来但未被props声明的属性(包括事件)
  • 子组件的<button>通过v-bind="$attrs"继承所有外来属性,自然包括父组件绑定的@click
  • 事件触发时直接冒泡到父组件,省去自定义事件中转站
3.2 进阶技巧:多个元素的事件分配

如果子组件包含多个交互元素,可用事件分割绑定

子组件 SearchBox.vue

<template>
  <input v-bind="inputAttrs" placeholder="敲回车搜索" />
  <button v-bind="buttonAttrs">搜索</button>
</template>
<script setup>
import { useAttrs } from 'vue'

const attrs = useAttrs()
// 拆分不同元素的事件
const inputAttrs = { 
  onKeypress: attrs.onKeypress 
}
const buttonAttrs = { 
  onClick: attrs.onClick 
}
</script>

父组件 Parent.vue

<template>
  <SearchBox 
    @keypress.enter="handleSearch" 
    @click="handleClick" 
  />
</template>

四、组合API版:用useAttrs()花式整活

组合API提供了更精细的事件操控能力。重点工具:useAttrs() 函数。

4.1 监听特定原生事件

比如在子组件中单独监听回车键:

子组件 SmartInput.vue

<template>
  <input v-bind="inputAttrs" />
</template>
<script setup>
import { useAttrs, onMounted } from 'vue'

const attrs = useAttrs()

// 手动监听回车事件
onMounted(() => {
  const handleKeypress = (e) => {
    if (e.key === 'Enter') {
      attrs.onEnter?.(e) // 调用父组件传递的回调
    }
  }
  document.addEventListener('keypress', handleKeypress)
})
</script>

父组件 Parent.vue

<template>
  <!-- 自定义事件名随心定义 -->
  <SmartInput @enter="handleEnter" />
</template>
4.2 事件过滤神器

只暴露指定事件给父组件,避免属性污染:

<script setup>
import { useAttrs, computed } from 'vue'

const attrs = useAttrs()

// 只允许click和keypress事件通过
const filteredAttrs = computed(() => ({
  onclick: attrs.onClick,
  onkeypress: attrs.onKeypress
}))
</script>

五、避坑指南:事件绑定的玄学现场

  1. 事件不触发?
    • 检查子组件根元素是否继承$attrs
    • 尝试用inheritAttrs: false关闭自动继承后手动绑定
  1. 组合API中attrs不是响应式
    • 如需响应式变化,用watchEffect监听特定属性
  1. 原生事件与自定义事件冲突
    • defineEmits定义的事件优先级更高,原生事件会被覆盖
  1. TS类型提示
    给事件加类型标注:
const attrs = useAttrs<{
  onSearch?: (value: string) => void
}>()

六、总结:什么时候该用这招?

推荐场景
✅ 封装基础UI组件(按钮、输入框)
✅ 需要批量传递事件时
✅ 组合API中追求代码简洁性

传统emit仍有用武之地
✅ 需要复杂数据预处理时
✅ 事件需要跨层级传递时
✅ 需要事件验证逻辑时

记住技术选型心法:“原生事件绑定是捷径,自定义事件是正道,根据场景选兵器才是高手”

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

值引力

持续创作,多谢支持!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值