События из компонента
У пунктов меню теперь есть кнопки «Заказать», а заказ хранится в App.vue: реф order и функция addToOrder(id), которая добавляет в него позицию с этим id или ещё одну такую же, если она там уже есть. Но кнопки находятся в MenuItem, а компонент не может менять данные родителя: у него нет order, а его пропсы доступны только для чтения.
Поэтому компонент сообщает родителю, что что-то произошло, с помощью события, так же как кнопка сообщает о нажатии событием click. Свои события компонент объявляет через defineEmits, который, как и defineProps, не нужно импортировать. А отправляет событие функцией, которую возвращает defineEmits; её обычно называют emit:
<script setup>
defineProps({
item: { type: Object, required: true },
});
const emit = defineEmits(["order"]);
</script>
<template>
<li class="item">
<span>{{ item.name }}</span>
<button @click="emit('order', item.id)">Заказать</button>
</li>
</template>emit('order', item.id) отправляет событие order (в Vue говорят, что компонент генерирует его) со значением — id позиции. Родитель слушает его, как любое другое событие, через @:
<MenuItem v-for="item in menu" :key="item.id" :item="item" @order="addToOrder" />Когда приходит событие, Vue вызывает addToOrder со значением события, поэтому id — это id той позиции, на кнопку которой нажали. Компонент не знает, что родитель с этим делает. Он лишь говорит: «эту позицию заказали», а что дальше, решает родитель.
emit работает и в скрипте, например в функции, которую вызывает кнопка. В скрипте пропсы лежат в объекте, который возвращает defineProps:
const props = defineProps({
item: { type: Object, required: true },
});
const emit = defineEmits(["order"]);
function order() {
emit("order", props.item.id);
}Типичные ошибки:
- Событие, которое родитель слушает под другим именем, например
@orderedдляemit('order'), не дойдёт. Ничего не произойдёт, и консоль тоже промолчит, поэтому проверяйте, что имена совпадают. - Событие, которого нет в
defineEmits, всё равно работает, но когда компонент его генерирует, Vue предупреждает:[Vue warn]: Component emitted event "order" but it is neither declared in the emits option nor as an "onOrder" prop.Объявляйте каждое событие: список подсказывает всякому, кто читает компонент, что тот умеет «говорить».
Задание
- 1
В
MenuItem.vueобъявите событиеorderи генерируйте его сidпозиции, когда нажата кнопка «Заказать». - 2
В
App.vueслушайте это событие наMenuItemи обрабатывайте его функциейaddToOrder. Закажите один капучино и два морковных торта: в заказе появятсяКапучино ×1иМорковный торт ×2.