Чтобы закрепить все пройденные темы курса и на практике увидеть, как Vue.js 3, Composables и Pinia могут работать вместе, давайте создадим небольшое, но небанальное приложение для управления задачами. Это не просто классическое ToDo, где вы добавляете и удаляете задачи — мы сделаем несколько интересных фич, таких как фильтрация по статусам, возможность приоритизации задач и работа с локальным хранилищем.
Что мы реализуем
- Добавление задач: Пользователь сможет добавить новую задачу, указав её текст и приоритет.
- Фильтрация задач: Фильтрация по статусам (все, активные, выполненные).
- Изменение статуса задачи: Возможность отметить задачу как выполненную.
- Приоритизация: Важные задачи будут выделяться в списке.
- Сохранение задач в локальное хранилище: Чтобы задачи не терялись после перезагрузки страницы.
Шаг 1: Подготовка проекта
Используя Vite, создаём новый проект:
npm init vite@latest vue-todo --template vue-ts
cd vue-todo
npm install
Далее устанавливаем Pinia для управления состоянием:
npm install pinia
Шаг 2: Настройка Pinia
Создадим новый store для управления задачами. В файле stores/todoStore.ts:
import { defineStore } from 'pinia';
import { ref } from 'vue';
interface Task {
id: number;
text: string;
completed: boolean;
priority: 'low' | 'medium' | 'high';
}
export const useTodoStore = defineStore('todo', () => {
const tasks = ref<Task[]>([]);
const filter = ref<'all' | 'active' | 'completed'>('all');
function addTask(text: string, priority: 'low' | 'medium' | 'high') {
tasks.value.push({
id: Date.now(),
text,
completed: false,
priority,
});
}
function toggleTaskStatus(id: number) {
const task = tasks.value.find((task) => task.id === id);
if (task) {
task.completed = !task.completed;
}
}
function filteredTasks() {
if (filter.value === 'active') {
return tasks.value.filter(task => !task.completed);
} else if (filter.value === 'completed') {
return tasks.value.filter(task => task.completed);
}
return tasks.value;
}
function setFilter(newFilter: 'all' | 'active' | 'completed') {
filter.value = newFilter;
}
return { tasks, filter, addTask, toggleTaskStatus, filteredTasks, setFilter };
});
Здесь мы создаём store для работы с задачами, где можно добавлять задачи, менять их статус и фильтровать по состоянию. Заметьте, что мы используем реактивные данные для задач и фильтра, что позволяет нам легко управлять их изменениями.
Шаг 3: Создание компонента для отображения задач
Теперь давайте создадим компонент для отображения списка задач и управления фильтрацией. В файле components/TodoList.vue:
<template>
<div>
<h1>Список задач</h1>
<div>
<label>
Фильтр:
<select v-model="filter">
<option value="all">Все</option>
<option value="active">Активные</option>
<option value="completed">Завершенные</option>
</select>
</label>
</div>
<ul>
<li v-for="task in filteredTasks" :key="task.id">
<input type="checkbox" v-model="task.completed" @change="toggleTaskStatus(task.id)" />
<span :style="{ fontWeight: task.priority === 'high' ? 'bold' : 'normal' }">{{ task.text }}</span>
<span> ({{ task.priority }})</span>
</li>
</ul>
</div>
</template>
<script setup>
import { useTodoStore } from '../stores/todoStore';
import { computed } from 'vue';
const todoStore = useTodoStore();
const filter = computed(() => todoStore.filter);
const filteredTasks = computed(() => todoStore.filteredTasks());
const toggleTaskStatus = todoStore.toggleTaskStatus;
</script>
Здесь мы реализовали список задач с возможностью фильтрации по статусу и изменению статуса задачи. Каждая задача отображается с её приоритетом — важные задачи выделены жирным шрифтом.
Шаг 4: Форма добавления задач
Добавим форму для добавления новых задач. В файле components/AddTask.vue:
<template>
<form @submit.prevent="submitForm">
<input v-model="taskText" placeholder="Введите задачу" />
<select v-model="taskPriority">
<option value="low">Низкий</option>
<option value="medium">Средний</option>
<option value="high">Высокий</option>
</select>
<button type="submit">Добавить</button>
</form>
</template>
<script setup>
import { ref } from 'vue';
import { useTodoStore } from '../stores/todoStore';
const taskText = ref('');
const taskPriority = ref('low');
const todoStore = useTodoStore();
function submitForm() {
if (taskText.value.trim()) {
todoStore.addTask(taskText.value, taskPriority.value);
taskText.value = '';
taskPriority.value = 'low';
}
}
</script>
Эта форма позволяет пользователю ввести текст задачи, выбрать её приоритет и добавить задачу в общий список. При отправке формы данные передаются в Pinia store.
Шаг 5: Подключение компонентов
Теперь соберём всё вместе в основном приложении. В файле App.vue:
<template>
<div>
<AddTask />
<TodoList />
</div>
</template>
<script setup>
import AddTask from './components/AddTask.vue';
import TodoList from './components/TodoList.vue';
</script>
Шаг 6: Сохранение задач в локальное хранилище
Чтобы задачи сохранялись между перезагрузками страницы, добавим работу с localStorage. Изменим наш store, добавив логику сохранения и загрузки данных:
import { defineStore } from 'pinia';
import { ref, watch } from 'vue';
interface Task {
id: number;
text: string;
completed: boolean;
priority: 'low' | 'medium' | 'high';
}
export const useTodoStore = defineStore('todo', () => {
const tasks = ref<Task[]>(JSON.parse(localStorage.getItem('tasks') || '[]'));
const filter = ref<'all' | 'active' | 'completed'>('all');
function addTask(text: string, priority: 'low' | 'medium' | 'high') {
tasks.value.push({
id: Date.now(),
text,
completed: false,
priority,
});
}
function toggleTaskStatus(id: number) {
const task = tasks.value.find((task) => task.id === id);
if (task) {
task.completed = !task.completed;
}
}
function filteredTasks() {
if (filter.value === 'active') {
return tasks.value.filter(task => !task.completed);
} else if (filter.value === 'completed') {
return tasks.value.filter(task => task.completed);
}
return tasks.value;
}
function setFilter(newFilter: 'all' | 'active' | 'completed') {
filter.value = newFilter;
}
watch(tasks, (newTasks) => {
localStorage.setItem('tasks', JSON.stringify(newTasks));
}, { deep: true });
return { tasks, filter, addTask, toggleTaskStatus, filteredTasks, setFilter };
});
Теперь задачи будут сохраняться в локальном хранилище, и при перезагрузке страницы они не исчезнут.
Заключение
Вот и всё! Мы создали небольшое, но полезное приложение для управления задачами с использованием Vue.js 3, Pinia и composables. В ходе работы мы затронули важные концепты Vue 3, такие как реактивность, компоненты, управление состоянием через Pinia, а также применили локальное хранилище для хранения данных. Это приложение можно легко расширять, добавляя новые функции, например, возможность редактировать задачи или устанавливать сроки выполнения.
[…] Дальше мы закрепим полученные знания из курса. Будет разрабатывать Todolist! […]