Vue中父子组件事件监听

父子组件事件监听

上一节中提到的<BlogPost> 组件。有时候它需要与父组件进行交互。例如,要在此处实现无障碍访问的需求,将博客文章的文字能够放大,而页面的其余部分仍使用默认字号。在父组件中,可以添加一个 postFontSize ref引用数据来实现动态控制字体大小的效果:

// 父组件中的属性定义
const posts = ref([
  /* ... */
])

const postFontSize = ref(1) // 字体大小默认为1em

在父组件模板中用它来控制所有博客文章的字体大小:

<!-- 父组件中的模板 -->
<div :style="{ fontSize: postFontSize + 'em' }">
  <BlogPost
    v-for="post in posts"
    :key="post.id"
    :title="post.title"
   />
</div>

然后,给 <BlogPost> 子组件添加一个按钮:

<!-- BlogPost.vue, 省略了 <script> -->
<template>
  <div class="blog-post">
    <h4>{{ title }}</h4>
    <button>Enlarge text</button>
  </div>
</template>
$emit方法触发自定义监听事件

这个按钮目前还没有做任何事情,我们想要点击这个按钮来告诉父组件它应该放大所有博客文章的文字。要解决这个问题,组件实例提供了一个自定义事件系统。父组件可以通过 v-on@ 来选择性地监听子组件上抛的事件,就像监听原生 DOM 事件那样:

<script setup>
// 定义一个事件处理函数
function changeFontsize(){
    postFontSize.value += 0.1
}
</script>
<template>
<BlogPost
  ...
  <!-- 父组件用于监听子组件中抛出的enlarge-text事件 -->
  <!-- 为事件监听器@enlarge-text(v-on:enlarge)设置事件响应处理函数(内联函数):postFontSize += 0.1 -->
  @enlarge-text="postFontSize += 0.1" 
  <!-- 传递一个事件处理函数-->
  <!-- @enlarge-text="changeFontSize"-->
 />
</template>

子组件可以通过调用内置的 $emit 方法,通过传入事件名称来抛出enlarge-text事件,因为有了 @enlarge-text="postFontSize += 0.1" 的事件监听器,父组件会接收到事件,则会迅速更新 postFontSize 的值:

<!-- BlogPost.vue, 省略了 <script> -->
<template>
  <div class="blog-post">
    <h4>{{ title }}</h4>
    <!-- 子组件通过$emit方法触发enlarge-text自定义事件 -->
    <button @click="$emit('enlarge-text')">Enlarge text</button> 
  </div>
</template>

和原生DOM事件不同,组件触发的事件没有冒泡机制。组件只能监听直接子组件触发的事件。平级组件或是跨越多层嵌套的组件间通信,应使用外部事件总线,或是使用全局状态管理方案

defineEmits()注册自定义监听事件

在组合式API中,则可以通过 defineEmits 宏来声明需要抛出的事件:

<!-- BlogPost.vue -->
<script setup>
defineProps(['title'])
defineEmits(['enlarge-text'])
</script>

这声明了一个组件可能触发的所有事件,还可以对事件的参数进行验证。同时,这还可以让 Vue 避免将它们作为原生事件监听器隐式地应用于子组件的根元素。

defineProps 宏类似,defineEmits 宏仅可用于 <script setup> 之中,并且不需要导入,它返回一个等同于 $emit 方法的 emit 函数。它可以被用于在组件的 <script setup> 中抛出事件,因为此处无法直接访问 $emit

<script setup>
defineProps(['title'])
const emit = defineEmits(['enlarge-text']) // 使用defineEmits宏注册事件
const emitCustomEvent = ()=>{
    emit('enlarge-text') // 抛出事件
}
</script>
<!-- BlogPost.vue, 省略了 <script> -->
<template>
  <div class="blog-post">
    <h4>{{ title }}</h4>
    <!-- 子组件通过$emit方法触发enlarge-text自定义事件 -->
    <button @click="emitCustomEvent">Enlarge text</button> 
  </div>

在选项式API中,可以通过 emits 选项定义组件会抛出的事件。可以从 setup() 函数的第二个参数,即 setup 上下文对象ctx上访问到 emit 函数:

export default {
  emits: ['enlarge-text'], //注册事件
  setup(props, ctx) {
    ctx.emit('enlarge-text') //抛出事件
  }
}
defineEmits()函数参数对象中的选项配置——自定义事件校验

与对 props 添加类型校验的方式类似,所有触发的事件也可以使用对象形式来描述。要为事件添加校验,则为事件赋值为一个函数,其接受的参数就是抛出事件时传入 emit 的内容,返回一个布尔值来表明事件是否合法。

<script setup>
  const emit = defineEmits({
  // 没有校验
  click: null, 
  // 校验submit表单自动提交事件
  submit: ({ email, password }) => {
        if (email && password) {
          return true
        } else {
          console.warn('Invalid submit event payload!')
          return false
        }
      }
    })

  function submitForm(email, password) {
     emit('submit', { email, password })
  }
</script>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

南腔北调-pilmar

你的鼓励将是我创作的最大动力!

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

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

打赏作者

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

抵扣说明:

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

余额充值