Vue 事件
在 Vue 中,父组件通过 props 向子组件传递数据,但子组件如何将信息传递回父组件呢?答案是使用自定义事件。子组件可以触发 (emit) 一个事件,而父组件可以像监听原生 DOM 事件一样监听 (listen) 这个事件。
触发与监听事件
子组件可以使用内置的 $emit 方法,通过传入事件名称来触发一个事件。
MyComponent.vue (子组件)
<script setup>
// 使用 defineEmits 宏来声明该组件会触发的事件
const emit = defineEmits(['someEvent'])
function handleClick() {
// 触发 'someEvent' 事件
emit('someEvent')
}
</script>
<template>
<button @click="handleClick">Click me</button>
</template>父组件可以使用 v-on (简写为 @) 来监听子组件触发的事件。
App.vue (父组件)
<script setup>
import MyComponent from './MyComponent.vue'
function onSomeEvent() {
console.log('someEvent was emitted!')
}
</script>
<template>
<MyComponent @some-event="onSomeEvent" />
</template>注意,与 prop 的命名转换类似,事件名称也存在自动的大小写转换。因为 HTML attribute 是大小写不敏感的,所以我们推荐使用 kebab-case (短横线分隔) 的事件监听器。如果在子组件中触发的是驼峰式 (camelCase) 的事件 someEvent,在父组件中可以用 some-event 来监听。
传递参数
有时,在触发事件时向其传递特定的数据是很有用的。例如,我们可能想让 <BlogPost> 组件告诉父组件这篇博文应该放大多少字号。
BlogPost.vue (子组件)
<script setup>
const emit = defineEmits(['enlarge-text'])
function enlargeText() {
// 触发事件并传递一个值
emit('enlarge-text', 0.1)
}
</script>
<template>
<button @click="enlargeText">Enlarge text</button>
</template>然后,当父组件监听这个事件时,可以通过一个内联的箭头函数来接收这个值。这个值会作为该函数的第一个参数。
App.vue (父组件)
<script setup>
import { ref } from 'vue'
import BlogPost from './BlogPost.vue'
const postFontSize = ref(1)
</script>
<template>
<div :style="{ fontSize: postFontSize + 'em' }">
<BlogPost @enlarge-text="(amount) => postFontSize += amount" />
</div>
</template>如果你希望事件处理器是一个方法,那么被传递的参数会作为该方法的第一个参数传入。
<script setup>
// ...
function onEnlargeText(amount) {
postFontSize.value += amount
}
</script>
<template>
<BlogPost @enlarge-text="onEnlargeText" />
</template>事件校验
与 props 类似,我们也可以对触发的事件进行校验。要添加校验,事件需要被定义为一个对象,而不是字符串数组。
<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() {
emit('submit', { email: 'user@example.com', password: '123' })
}
</script>如果校验失败,Vue 会在控制台打印一个警告信息。
v-model 与组件
自定义事件也可以用于创建支持 v-model 的自定义输入组件。一个组件上的 v-model 默认会使用 modelValue 作为 prop 和 update:modelValue 作为事件。我们可以通过在子组件中实现这两点来让它支持 v-model。
CustomInput.vue
<script setup>
defineProps(['modelValue'])
defineEmits(['update:modelValue'])
</script>
<template>
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
/>
</template>现在,父组件就可以在 CustomInput 上使用 v-model 了。
App.vue
<script setup>
import { ref } from 'vue'
import CustomInput from './CustomInput.vue'
const searchText = ref('hello')
</script>
<template>
<CustomInput v-model="searchText" />
<p>{{ searchText }}</p>
</template>