Антон Ларичев

Введение
Vue 3 — современный прогрессивный фреймворк для построения пользовательских интерфейсов. Он сочетает простоту входа с мощными возможностями для крупных приложений. В этой статье мы разберём базовые концепции Vue 3 и напишем первое приложение — менеджер задач с реактивным состоянием, обработкой событий и компонентами. Всё это займёт около часа.
Vue 3 принёс несколько ключевых нововведений: Composition API, улучшенную систему реактивности на основе Proxy, фрагменты и Teleport. Мы сосредоточимся на Composition API как на современном способе писать компоненты.
Установка и создание проекта
Для старта понадобится Node.js версии 18 или выше. Создадим проект через официальный инструмент create-vue:
# Создаём новый проект Vue 3
npm create vue@latest my-first-app
# Переходим в папку проекта
cd my-first-app
# Устанавливаем зависимости
npm install
# Запускаем dev-сервер
npm run dev
При создании проекта инструмент задаст вопросы о подключении TypeScript, JSX, Vue Router и других опций. Для первого приложения достаточно базовой конфигурации без дополнений.
После запуска по адресу http://localhost:5173 откроется стартовая страница. Структура проекта проста: папка src содержит точку входа main.js, корневой компонент App.vue и директорию components для дочерних компонентов.
Реактивность и Composition API
Главная идея Vue — реактивность. Когда состояние меняется, UI обновляется автоматически. В Composition API для создания реактивных значений используются функции ref и reactive.
<script setup>
import { ref, computed } from 'vue'
// Создаём реактивную переменную для текста новой задачи
const newTask = ref('')
// Реактивный массив задач
const tasks = ref([])
// Вычисляемое значение — количество задач
const tasksCount = computed(() => tasks.value.length)
// Функция добавления задачи в список
function addTask() {
// Проверяем, что строка не пустая
if (newTask.value.trim() === '') return
// Добавляем объект задачи в массив
tasks.value.push({
id: Date.now(),
text: newTask.value,
done: false
})
// Очищаем поле ввода
newTask.value = ''
}
</script>
Обратите внимание: к значению ref внутри <script> обращаемся через .value, а в шаблоне Vue делает это автоматически. Директива <script setup> — синтаксический сахар, позволяющий писать компоненты компактнее.
Шаблоны и директивы
Шаблон Vue — это обычный HTML с расширениями: интерполяцией {{ }}, директивами v-if, v-for, v-model и событиями через @click.
<template>
<div class="app">
<h1>Менеджер задач</h1>
<!-- Двусторонняя привязка с реактивной переменной -->
<input
v-model="newTask"
@keyup.enter="addTask"
placeholder="Новая задача"
/>
<button @click="addTask">Добавить</button>
<!-- Условный рендеринг сообщения -->
<p v-if="tasksCount === 0">Задач пока нет</p>
<!-- Перебор массива задач -->
<ul>
<li v-for="task in tasks" :key="task.id">
<input type="checkbox" v-model="task.done" />
<span :class="{ done: task.done }">{{ task.text }}</span>
</li>
</ul>
<p>Всего задач: {{ tasksCount }}</p>
</div>
</template>
<style scoped>
.done {
text-decoration: line-through;
color: gray;
}
</style>
Атрибут scoped у тега <style> ограничивает действие стилей текущим компонентом. Это удобно: можно использовать общие имена классов без конфликтов.
Разделение на компоненты
Когда приложение растёт, разделите его на компоненты. Создадим отдельный компонент TaskItem.vue для одной задачи и передадим данные через props.
<!-- src/components/TaskItem.vue -->
<script setup>
// Объявляем входные параметры компонента
const props = defineProps({
task: {
type: Object,
required: true
}
})
// Объявляем события, которые компонент может эмитить
const emit = defineEmits(['toggle', 'remove'])
</script>
<template>
<li>
<input
type="checkbox"
:checked="task.done"
@change="emit('toggle', task.id)"
/>
<span :class="{ done: task.done }">{{ task.text }}</span>
<button @click="emit('remove', task.id)">Удалить</button>
</li>
</template>
В родительском компоненте импортируем и используем его:
<script setup>
import TaskItem from './components/TaskItem.vue'
// Функция переключения статуса задачи по id
function toggleTask(id) {
const task = tasks.value.find(t => t.id === id)
if (task) task.done = !task.done
}
// Функция удаления задачи из массива
function removeTask(id) {
tasks.value = tasks.value.filter(t => t.id !== id)
}
</script>
<template>
<TaskItem
v-for="task in tasks"
:key="task.id"
:task="task"
@toggle="toggleTask"
@remove="removeTask"
/>
</template>
Такой подход делает код переиспользуемым и тестируемым: компонент TaskItem не знает о бизнес-логике, он лишь отображает данные и сообщает о действиях пользователя.
Частые ошибки
Забыли про .value у ref. В скрипте к ref всегда обращаются через .value. Запись tasks.push(...) вместо tasks.value.push(...) приведёт к ошибке: push нет у объекта-обёртки.
Деструктуризация reactive-объекта. Если использовать reactive и затем разложить его через const { count } = state, реактивность теряется. Для сохранения связи применяйте toRefs(state).
Отсутствие атрибута key в v-for. Без уникального ключа Vue может неправильно обновлять список при изменениях. Используйте стабильный идентификатор, а не индекс массива.
Прямая мутация props. Дочерний компонент не должен изменять переданные props. Вместо этого эмитьте событие, а родитель обновит данные.
Смешивание Options API и Composition API без причины. Выбирайте один стиль для проекта. Composition API лучше масштабируется и удобнее с TypeScript.
Заключение
За час мы прошли путь от пустого проекта до работающего менеджера задач: настроили окружение, освоили реактивность через ref и computed, познакомились с шаблонами и директивами, разделили приложение на компоненты с props и emits.
Дальше стоит изучить Vue Router для навигации, Pinia для глобального состояния, асинхронные компоненты и серверный рендеринг через Nuxt. Vue 3 — это экосистема, в которой каждая следующая концепция логично надстраивается над предыдущей. Главное — практика: попробуйте расширить приложение фильтрами, локальным хранилищем и загрузкой задач с сервера.






Комментарии
0