Общее состояние
До сих пор данные, которые были нужны нескольким компонентам, жили в их родителе: он передавал их вниз через пропсы, а дети просили об изменениях через события, как в уроке «События компонентов». Для родителя и его детей это правильный путь. Но некоторые данные нужны по всему приложению: заказ, вошедший пользователь, корзина магазина. Передавать их вниз через каждый промежуточный компонент долго и утомительно.
Есть способ проще: поместить реф в отдельный модуль, вне любой функции, вместе с функциями, которые его меняют:
import { ref } from "vue";
export const order = ref([]);
export function addToOrder(item) {
const found = order.value.find((line) => line.id === item.id);
if (found) {
found.count++;
} else {
order.value.push({ ...item, count: 1 });
}
}Каждый компонент, которому нужен заказ, импортирует то, что использует:
<script setup>
import { order } from "./order.js";
</script>
<template>
<p>Позиций в заказе: {{ order.length }}</p>
</template>Это работает, потому что модуль выполняется только один раз. Первый import запускает order.js и создаёт реф, а все остальные import получают тот же самый реф. Поэтому, когда MenuItem.vue вызывает addToOrder(), он добавляет в тот самый заказ, который показывает OrderList.vue, а Vue обновляет список, как после любого изменения рефа.
Сравните с композаблом: там ref() находится внутри функции, поэтому каждый вызов создаёт новый реф, и у каждого компонента он свой. Здесь ref() снаружи, поэтому реф один, и все компоненты делят его. Разницу делает одна и та же строка, стоящая в другом месте.
Держите функции, которые меняют общее состояние, рядом с ним, в том же модуле, как addToOrder(). Тогда вы всегда знаете, где меняется заказ: где бы он ни менялся, это происходит через одну из них.
В больших приложениях общее состояние обычно выносят в Pinia — библиотеку от команды Vue, которая работает на той же идее и добавляет инструменты для поиска ошибок. Вы познакомитесь с ней после этого курса.
Задание
- 1
Заставьте кнопки
Заказатьработать. ВMenuItem.vueимпортируйтеaddToOrderиз./order.jsи вызывайте её с позицией при нажатии на кнопку. ВOrderList.vueимпортируйтеorderиз./order.jsвместо пустого массива. Закажите эспрессо дважды: в списке появится2 × Эспрессо. - 2
Добавьте возможность убрать строку. В
order.jsнапишите и экспортируйтеremoveFromOrder(id), которая с помощьюfilter()оставляет только строки с другимid. ВOrderList.vueдобавьте в каждую строку кнопку×, которая вызывает эту функцию. - 3
Покажите итог заказа в
App.vue, под заголовком:Итого: €8. Вorder.jsэкспортируйте вычисляемое значениеtotal, которое циклом складываетprice * countвсех строк, и импортируйте его вApp.vue.