Когда проект становится больше, неизбежно появляется вопрос: как писать интерфейс так, чтобы потом не переписывать одно и то же десятки раз? Ответ простой — нужно думать не о «страницах» или «кусочках верстки», а о компонентах.
Компонент — это маленький кусочек интерфейса, у которого есть:
- состояние (например, количество лайков),
- логика (что делать при клике),
- контракт — внешний API: методы или события, через которые компонент общается с другими частями приложения.
Давайте посмотрим, как на примере самой простой вещи — кнопки лайк ❤️ — можно построить компонент, который будет переиспользуемым, гибким и легко встраиваемым в разные проекты.
Начнём с кнопки
Сначала определим саму кнопку. Она должна показывать количество лайков и увеличивать его при клике.
class LikeButton {
constructor(el, { api, notifier }) {
this.el = el; // контейнер для кнопки
this.api = api; // сервис для запросов
this.notifier = notifier; // сервис для сообщений
this.count = 0; // начальное количество лайков
this.render(); // отрисовать кнопку
this.bind(); // повесить обработчики
}
render() {
// вставляем HTML кнопки с текущим счётчиком
this.el.innerHTML = `<button class="like-btn">❤️ ${this.count}</button>`;
}
bind() {
// при клике вызываем метод like()
this.el.querySelector('.like-btn')
.addEventListener('click', () => this.like());
}
async like() {
this.count++; // увеличиваем число
this.render(); // перерисовываем кнопку
try {
// отправляем данные на сервер
await this.api.post('/like', { count: this.count });
} catch (err) {
// если ошибка — показываем сообщение
this.notifier.show('Не удалось поставить лайк');
}
}
}
Здесь всё максимально просто: при клике мы увеличиваем счётчик и сразу же пробуем отправить данные на сервер. Если сервер ответил ошибкой — показываем сообщение пользователю.
Отдельный слой для работы с сервером
Чтобы не писать fetch внутри компонента, заведём отдельный класс Api. Он отвечает только за одно — отправку запросов.
class Api {
constructor(baseUrl) {
this.baseUrl = baseUrl; // базовый адрес сервера
}
async post(path, data) {
// отправляем POST-запрос
const res = await fetch(this.baseUrl + path, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data), // превращаем объект в JSON
});
// если ошибка — бросаем исключение
if (!res.ok) throw new Error(res.statusText);
// возвращаем результат как объект
return res.json();
}
}
Теперь, если поменяется сервер или формат запросов, нам не придётся лезть внутрь LikeButton. Достаточно обновить Api.
Сообщения пользователю
Сообщения тоже лучше вынести в отдельный сервис. Самый простой вариант — использовать обычный alert:
class Notifier {
show(msg) {
alert(msg);
}
}
📢 Подписывайтесь на наш Telegram-канал.
Там вы найдете анонсы обучающих статей и видео, готовый код для ваших проектов и увлекательные курсы. Ничего лишнего — только практика, вдохновение и развитие.
Но в реальном проекте здесь можно подключить всплывашки, toast-уведомления или даже кастомный popup. Кнопка лайк ничего про это знать не будет.
Как собрать всё вместе
В HTML мы просто размечаем контейнеры для кнопок:
<div class="like"></div>
<div class="like"></div>
<div class="like"></div>
А в коде инициализируем:
document.addEventListener('DOMContentLoaded', () => {
const api = new Api('/api'); // создаём API-клиент
const notifier = new Notifier(); // создаём уведомления
// ищем все контейнеры с классом .like и навешиваем кнопки
document.querySelectorAll('.like').forEach(el => {
new LikeButton(el, { api, notifier }); // подключаем компонент
});
});
И вот у нас уже три независимые кнопки лайк, каждая работает сама по себе, но все используют общий API и общий сервис уведомлений.
Почему это удобно
Получается, что у нас есть три слоя:
- LikeButton отвечает только за интерфейс и поведение кнопки.
- Api знает только про работу с сервером.
- Notifier отвечает за сообщения.
Они связаны через параметры, а не жёстко «зашиты» друг в друга. Это и есть ключ к переиспользованию.
Сегодня у нас кнопка под статьёй, завтра — под фотографией в галерее. Компонент остаётся тем же, просто рендерится в другом месте.
Итог
Чтобы писать переиспользуемые фронтенд-компоненты, нужно соблюдать простые правила:
- Компонент делает только свою задачу и не берёт на себя лишнего.
- Всё внешнее (запросы, сообщения, хранилища) он получает снаружи.
- Сборка происходит в одном месте, где мы создаём зависимости и связываем их вместе.
В результате компоненты становятся похожи на кубики LEGO. Их можно брать, комбинировать, менять местами и использовать снова и снова — без переписывания логики.
Комментарии
0