跳转到内容

组件事件

本页面假设您已经阅读了组件基础知识。如果您是组件的新手,请先阅读。

发出和监听事件

组件可以直接在模板表达式(例如,在v-on处理器中)中使用内置的$emit方法发出自定义事件

模板
<!-- MyComponent -->
<button @click="$emit('someEvent')">Click Me</button>

$emit()方法也作为this.$emit()在组件实例上可用

js
export default {
  methods: {
    submit() {
      this.$emit('someEvent')
    }
  }
}

父组件可以使用v-on监听它

模板
<MyComponent @some-event="callback" />

组件事件监听器也支持.once修饰符

模板
<MyComponent @some-event.once="callback" />

与组件和props一样,事件名称提供自动的大小写转换。注意我们发出的是camelCase事件,但可以在父组件中使用kebab-cased监听器来监听它。与props的大小写一样,我们建议在模板中使用kebab-cased事件监听器。

提示

与原生DOM事件不同,组件发出的事件不会冒泡。您只能监听直接子组件发出的事件。如果需要在不同兄弟组件或深层嵌套组件之间进行通信,请使用外部事件总线或全局状态管理解决方案

事件参数

有时,在事件中发出特定值很有用。例如,我们可能希望<BlogPost>组件负责调整文本的大小。在这些情况下,我们可以向$emit传递额外的参数以提供此值

模板
<button @click="$emit('increaseBy', 1)">
  Increase by 1
</button>

然后,当我们监听父组件中的事件时,我们可以使用内联箭头函数作为监听器,这允许我们访问事件参数

模板
<MyButton @increase-by="(n) => count += n" />

或者,如果事件处理器是一个方法

模板
<MyButton @increase-by="increaseCount" />

那么值将作为该方法的第一个参数传递

js
methods: {
  increaseCount(n) {
    this.count += n
  }
}
js
function increaseCount(n) {
  count.value += n
}

提示

所有传递给$emit()的额外参数(在事件名称之后)都将转发给监听器。例如,使用$emit('foo', 1, 2, 3)时,监听函数将接收到三个参数。

声明事件

组件可以使用defineEmits()宏(defineEmits())显式声明它将发出的事件emits选项

vue
<script setup>
defineEmits(['inFocus', 'submit'])
</script>

我们在<template>中使用的$emit方法在组件的<script setup>部分中不可用,但defineEmits()返回一个等效函数,我们可以用它来代替。

vue
<script setup>
const emit = defineEmits(['inFocus', 'submit'])

function buttonClick() {
  emit('submit')
}
</script>

defineEmits()不能在函数中使用,它必须直接放在上面的示例中<script setup>内。

如果你使用的是显式的setup函数而不是<script setup>,则应使用emits选项来声明事件,并且emit函数将在setup()上下文中暴露。

js
export default {
  emits: ['inFocus', 'submit'],
  setup(props, ctx) {
    ctx.emit('submit')
  }
}

setup()上下文的其他属性一样,emit可以安全地进行解构。

js
export default {
  emits: ['inFocus', 'submit'],
  setup(props, { emit }) {
    emit('submit')
  }
}
js
export default {
  emits: ['inFocus', 'submit']
}

emits选项和defineEmits()宏也支持对象语法。如果你使用TypeScript,你可以对参数进行类型注解,这使得我们可以在运行时验证发出事件的负载。

vue
<script setup lang="ts">
const emit = defineEmits({
  submit(payload: { email: string, password: string }) {
    // return `true` or `false` to indicate
    // validation pass / fail
  }
})
</script>

如果你使用TypeScript与<script setup>一起使用,也可以使用纯类型注解来声明发出的事件。

vue
<script setup lang="ts">
const emit = defineEmits<{
  (e: 'change', id: number): void
  (e: 'update', value: string): void
}>()
</script>

更多信息:组件发出事件的类型注解

js
export default {
  emits: {
    submit(payload: { email: string, password: string }) {
      // return `true` or `false` to indicate
      // validation pass / fail
    }
  }
}

另请参阅:组件发出事件的类型注解

虽然这不是必需的,但建议定义所有发出的事件,以便更好地记录组件应该如何工作。这也允许Vue排除已知的监听器从穿透属性,避免由第三方代码手动派发的DOM事件引起的边缘情况。

提示

如果将原生事件(例如,click)定义在emits选项中,监听器现在将仅监听组件发出的click事件,而不再响应原生click事件。

事件验证

与属性类型验证类似,如果使用对象语法而不是数组语法定义,则可以验证发出的事件。

要添加验证,将事件分配给一个函数,该函数接收传递给this.$emitemit调用的参数,并返回一个布尔值以指示事件是否有效。

vue
<script setup>
const emit = defineEmits({
  // No validation
  click: null,

  // Validate submit event
  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>
js
export default {
  emits: {
    // No validation
    click: null,

    // Validate submit event
    submit: ({ email, password }) => {
      if (email && password) {
        return true
      } else {
        console.warn('Invalid submit event payload!')
        return false
      }
    }
  },
  methods: {
    submitForm(email, password) {
      this.$emit('submit', { email, password })
    }
  }
}
组件事件已加载