</> Praktum

Главная / Vue / Компоненты

Урок 3 · Vue · 20 минут

Компоненты Vue: собираем интерфейс из частей

Компонент — самостоятельный фрагмент интерфейса со своей разметкой и логикой. Карточку товара или пункт меню пишут один раз, а используют столько раз, сколько нужно.

Данные через props

<script setup>
defineProps({ title: String })
</script>
<template><h2>{{ title }}</h2></template>

Родитель передаёт компоненту данные: <LessonCard title="Vue" />. Компонент не должен менять prop: отправляй событие родителю, если требуется обновление.

Когда выносить компонент

Выноси блок, если он повторяется, имеет собственную задачу или мешает читать большую страницу. Не нужно дробить каждую строку: у компонента должна быть понятная роль.

Ошибки

  • Менять prop внутри дочернего компонента.
  • Не давать компоненту понятное имя из двух слов.
  • Передавать слишком много несвязанных props.

Props приходят сверху, события уходят наверх

Дочерний компонент получает данные через defineProps и не меняет их. О клике он сообщает через emit. Родитель решает, удалять элемент или нет, потому что список живёт у родителя. Так одна карточка не обязана знать, откуда взялся весь каталог.

Имена props в шаблоне обычно пишут через дефис, в скрипте — camelCase. lesson-id в разметке станет lessonId в объекте props. Это легко перепутать в первый вечер.

<script setup>
defineProps({
  title: { type: String, required: true },
  minutes: { type: Number, default: 10 }
});
const emit = defineEmits(["remove"]);
</script>

<template>
  <article>
    <h2>{{ title }}</h2>
    <button type="button" @click="emit('remove')">Убрать</button>
  </article>
</template>

Слот оставляет содержимое родителю

Если каждая карточка внутри разная, не плоди десяток флагов. Оставь в карточке рамку и заголовок, а тело передай слотом. Родитель напишет между тегами компонента свою разметку. Именованные слоты нужны, когда таких мест несколько: шапка, действие, подвал.

Как делить без крошек

  • Компонент можно объяснить фразой и дать ему свои данные на вход.
  • Он не тянет сам список с сервера, если тем же списком пользуются соседи.
  • Стили карточки не зависят от того, что она обязательно лежит в конкретной секции.
  • Один компонент не читает внутренности другого через селектор.
Запомни: Сначала собери экран в одном файле. Вынеси компонент на втором повторении, а не до первой работающей кнопки.

Практика

Сделай компонент карточки урока с props title и level.

Решение
defineProps({ title: String, level: String })

Как компоненты обмениваются данными

Родитель передаёт данные вниз через props, а ребёнок сообщает о действиях через emit. Такой однонаправленный поток облегчает поиск ошибок. Не изменяй prop внутри дочернего компонента: если нужно редактирование, создай локальное состояние или отправь событие родителю.

Слоты подходят для разметки, которую родитель должен вставить внутрь оболочки компонента. Это удобнее десятка props вроде headerText и footerText. Имя компонента должно отражать роль — LessonCard или BaseButton. Слишком универсальный компонент с множеством режимов сложнее поддерживать, чем несколько небольших и очевидных.

Разберём рабочий пример

<script setup>
defineProps({ title: String })
const emit = defineEmits(["open"])
</script>

<template>
  <article>
    <h2>{{ title }}</h2>
    <slot />
    <button @click="emit('open')">Открыть</button>
  </article>
</template>

Не копируй пример вслепую: измени данные, убери одну строку и посмотри, как меняется результат. Такой эксперимент быстрее показывает роль каждой части кода, чем простое чтение.

Проверка перед использованием

  • Props не изменяются внутри ребёнка.
  • События описывают действие пользователя.
  • Слоты используются для вложенной разметки.
  • Компонент не перегружен несвязанными режимами.