Компоненты
Props вниз, события вверх
Небольшой компонент с понятными входными данными и одним действием.
Компонент объединяет шаблон, логику и стили части интерфейса. В проекте со сборкой их удобно хранить в одном файле .vue. Хорошая граница компонента определяется его задачей: карточка статьи, поле формы или панель фильтров.
Явный контракт
Родитель передаёт данные через props. Дочерний компонент сообщает о действиях событиями. Props имеют однонаправленный поток: дочерний компонент не должен присваивать новое значение входному prop. Изменение вложенных свойств переданного объекта тоже может неожиданно затронуть родителя.
<script setup>
defineProps({ title: String, saved: Boolean })
const emit = defineEmits(['save'])
</script>
<template>
<article>
<h2>{{ title }}</h2>
<button :disabled="saved" @click="emit('save')">
{{ saved ? 'Сохранено' : 'Сохранить' }}
</button>
</article>
</template>
Кто хранит состояние
Если несколько карточек влияют на общий список, храните этот список у общего родителя. Родитель обработает @save и передаст обновлённый saved обратно. Локальное состояние подходит для деталей, которые не нужны остальной странице.
Практика
Соберите список из трёх карточек. Покажите над ним число сохранённых материалов и проверьте, что повторное нажатие не увеличивает счётчик.