在 Vue 3 中使用 emit,子组件可以将事件通知父组件,父组件可以在响应这些事件时执行特定的逻辑。
emit 是一种非常灵活的通信方式,允许组件之间以解耦的方式进行交互。
1. 基本用法
1、使用 defineEmits
子组件
<template>
<div style="border: 1px solid orange">
<p>Child</p>
<button @click="handleClick" style="border: none;">Click Me</button>
</div>
</template>
<script setup>
import { defineEmits } from 'vue'
const emit = defineEmits(['update']) // 定义事件
const handleClick = () => {
emit('update', 'newValue') // 触发 'update' 事件,并传递参数
}
</script>
父组件
<template>
<div style="border: 1px solid red">
<p>Father</p>
<Child @update="handleUpdate" />
</div>
</template>
<script setup>
import Child from './Child.vue'
const handleUpdate = (value) => {
console.log('Received from child:', value)
}
</script>
2、在模板中直接使用 emit
子组件
<template>
<div style="border: 1px solid orange">
<p>Child</p>
<button @click="$emit('update', 'newValue')" style="border: none;">Click Me</button>
</div>
</template>
父组件不变
3、在 JSX/TSX 中使用 emit
子组件
import { defineComponent } from 'vue';
export default defineComponent({
setup(_, { emit }) {
const handleClick = () => {
emit('update', 'newValue');
};
return () => (
<div style="border: 1px solid orange">
<p>Child</p>
<button style="border: none;" onClick={handleClick}>Click Me</button>
</div>
);
}
});
父组件
import { defineComponent } from 'vue';
import Child from './Child';
const Father = defineComponent({
setup() {
const handleUpdate = (value) => {
console.log('Received from child:', value)
}
return () => (
<div style="border: 1px solid red">
<p>Father</p>
<Child onUpdate={handleUpdate} />
</div>
);
},
});
export default Father;
2. 举例 🌰
子组件
import { defineComponent } from 'vue';
const Child = defineComponent({
props: ['label', 'message'],
emits: {
'update': (value: string) => typeof value === 'string',
'click': null,
},
setup(props, { emit }) {
const emitEvent = () => {
emit('update', 'Hello from Child!');
emit('click');
};
return () =>
<div style={{ border: '1px solid pink' }}>
<h3>Child Component Content</h3>
<button onClick={emitEvent} style={{ border: 'none' }}>{props.label}</button>
<div>props.message:{props.message}</div>
</div>
},
});
export default Child;
父组件
import { defineComponent, ref } from 'vue';
import Child from './Child';
const Father = defineComponent({
setup() {
const date = ref('2024-08-21');
const handleUpdate = (target: string) => {
console.log('Update event received:', target);
};
const handleClick = () => {
console.log('Click event received');
};
return () => (
<div>
<Child label="Click Me" message={date.value} onUpdate={handleUpdate} onClick={handleClick} />
</div>
);
},
});
export default Father;
父组件向子组件传递数据,子组件使用 prop 接收,进而展示到页面。
子组件向父组件抛出事件 emit,并且可以传递参数,父组件使用 onXXX 来监听子组件触发的事件。比如 onUpdate 和 onClick 事件。
3. 注意事项
1、事件名称区分大小写
Vue 3 中的事件名称是区分大小写的。这意味着 @update 和 @Update 是两个不同的事件名称。在子组件和父组件中,确保事件名称的一致性非常重要。
2、事件参数传递
使用 emit 可以传递多个参数,这些参数将在父组件中对应的事件处理函数中接收到,并且需要按照顺序正确接收它们。
// 子组件
emit('update', 'value1', 'value2');
// 父组件
<Child @update="handleUpdate" />
<script setup>
const handleUpdate = (param1, param2) => {
console.log(param1, param2); // param1 = 'value1', param2 = 'value2'
};
</script>
3、事件参数类型
在使用 TypeScript 时,defineEmits 可以定义事件和参数的类型。不仅可以提高代码的安全性,还能在开发过程中获得更好的类型提示。
const emit = defineEmits<{ (event: 'update', value: string): void }>();
emit('update', 'newValue'); // 正确
emit('update', 123); // 错误,类型不匹配
4、确保事件已声明
通过 defineEmits 定义子组件中的事件时,要确保所有可能触发的事件都已声明。未声明的事件将无法通过 emit 触发。
const emit = defineEmits(['update', 'delete']);
emit('update', 'newValue'); // 正确
emit('delete'); // 正确
emit('add'); // 错误,未声明 'add' 事件
5、避免滥用事件
在设计组件时,尽量减少事件的种类和数量,尤其是在组件树较为复杂时。过多的事件可能导致代码难以维护和调试。