企业项目管理、ORK、研发管理与敏捷开发工具平台

网站首页 > 精选文章 正文

Vue深入组件:组件事件详解2_vue组件例子

wudianyun 2025-09-29 13:46:59 精选文章 14 ℃

声明触发的事件

为了让组件的用法更清晰(作为文档),同时让 Vue 能区分事件与透传 attribute,推荐显式声明组件要触发的事件。根据组件是否使用 <script setup>,声明方式有所不同。

使用 <script setup>时:defineEmits()宏

<script setup> 中,需通过 defineEmits() 宏声明事件。该宏无需导入,直接使用,返回一个与 $emit 作用相同的 emit 函数,供 <script> 中触发事件使用。

基础声明(数组形式)

  <script setup>
  // 声明组件会触发 inFocus 和 submit 事件
  const emit = defineEmits(['inFocus', 'submit'])
  
  // 在脚本中通过 emit 函数触发事件
  function buttonClick() {
    emit('submit') // 触发 submit 事件,与模板中 $emit 效果一致
  }
  </script>

注意:defineEmits() 必须直接放在 <script setup> 的顶级作用域中,不能在子函数内使用。

对象形式声明(支持参数校验)

defineEmits() 还支持对象形式声明,可通过函数对事件的参数进行校验(后续“事件校验”章节详细讲解)。若搭配 TypeScript,还能为参数指定类型:

  <script setup lang="ts">
  // 对象形式声明:submit 事件需接收 { email: string, password: string } 类型的参数
  const emit = defineEmits({
    submit(payload: { email: string, password: string }) {
      // 返回 true 表示校验通过,false 表示失败
      return payload.email && payload.password // 简单校验:邮箱和密码都存在则通过
    }
  })
  </script>

TypeScript 类型标注声明

若使用 TypeScript + <script setup>,可通过纯类型标注声明事件,语法更简洁,类型约束更严格:

  <script setup lang="ts">
  // 类型标注声明:定义事件的名称和参数类型
  const emit = defineEmits<{
    (e: 'change', id: number): void // change 事件需传递 number 类型的 id
    (e: 'update', value: string): void // update 事件需传递 string 类型的 value
  }>()
  
  // 触发事件时,TypeScript 会自动校验参数类型
  emit('change', 123) // 正确:id 是 number 类型
  emit('update', 'hello') // 正确:value 是 string 类型
  // emit('change', 'abc') // 错误:TypeScript 会提示参数类型不匹配
  </script>

不使用 <script setup>时:emits选项

若未使用 <script setup>,需在组件选项中通过 emits 选项声明事件,同时 emit 函数会暴露在 setup() 的上下文对象(ctx)中。

基础用法

  export default {
    // 声明会触发的事件(数组形式)
    emits: ['inFocus', 'submit'],
    setup(props, ctx) {
      // 通过 ctx.emit 触发事件
      ctx.emit('submit')
    }
  }

解构 emit函数

setup() 上下文对象的 emit 可安全解构,简化使用:

  export default {
    emits: ['inFocus', 'submit'],
    // 解构 ctx 中的 emit
    setup(props, { emit }) {
      emit('submit') // 直接使用 emit 函数
    }
  }

声明事件的意义

显式声明事件虽非强制,但有重要作用:

  1. 作为组件文档:清晰告知使用者组件会触发哪些事件,提升代码可读性和可维护性。
  2. 区分事件与透传 attribute:Vue 会根据声明的事件过滤透传 attribute(避免将事件名当作 HTML 属性传递给子组件的根元素)。
  3. 屏蔽原生事件冲突:若原生事件名(如 click)被声明在 emits 中,父组件的监听器只会响应子组件触发的 click 事件,不再响应子组件根元素的原生 click 事件——避免了事件来源的混淆。

事件校验

与 props 类似,组件事件也可进行校验:通过在事件声明中定义函数,验证 emit 传递的参数是否合法。若校验失败,可在控制台抛出警告,帮助开发者发现问题。

事件校验的实现

在对象形式的事件声明中,将事件名对应的 value 设为一个函数:该函数接收 emit 传递的参数,返回 true(校验通过)或 false(校验失败)。

  <script setup>
  // 对象形式声明事件,同时定义校验逻辑
  const emit = defineEmits({
    // click 事件:null 表示无需校验
    click: null,
  
    // submit 事件:定义校验函数
    submit: ({ email, password }) => {
      // 校验逻辑:邮箱和密码都存在则通过
      if (email && password) {
        return true // 校验通过
      } else {
        // 校验失败:打印警告
        console.warn('Invalid submit event payload! 邮箱和密码不能为空')
        return false
      }
    }
  })
  
  // 触发 submit 事件的函数
  function submitForm(email, password) {
    emit('submit', { email, password }) // 传递参数,触发校验
  }
  </script>

当调用 submitForm('test@example.com', '123456') 时,参数符合校验规则(邮箱和密码都存在),校验通过;若调用 submitForm('', ''),则校验失败,控制台会输出警告。

校验函数的作用

事件校验主要用于开发阶段,帮助规范子组件向父组件传递的数据格式。例如:确保表单提交事件传递的参数包含必要字段(如邮箱、密码),避免父组件因接收无效数据而出现错误。生产环境中,校验逻辑不会影响事件的触发(即使校验失败,事件仍会被父组件接收),仅作为开发辅助。

通过组件事件,子组件能灵活地向父组件传递信息,而显式声明和校验则让事件交互更规范、可靠。合理使用组件事件,能让组件间的通信清晰且可维护,是构建复杂 Vue 应用的基础技能。

Tags:

最近发表
标签列表