Когда проект становится больше, неизбежно появляется вопрос: как писать интерфейс так, чтобы потом не переписывать одно и то же десятки раз? Ответ простой — нужно думать не о «страницах» или «кусочках верстки», а о компонентах.

Компонент — это маленький кусочек интерфейса, у которого есть:

  • состояние (например, количество лайков),
  • логика (что делать при клике),
  • контракт — внешний 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-канал.

Там вы найдете анонсы обучающих статей и видео, готовый код для ваших проектов и увлекательные курсы. Ничего лишнего — только практика, вдохновение и развитие.

👉 https://t.me/codelab_channel

Но в реальном проекте здесь можно подключить всплывашки, 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

Без регистрации и смс