Жизненный цикл веб-компонентов

Haunted — это библиотека для создания веб-компонентов с использованием функционального подхода и хуков, вдохновлённых React. Понимание жизненного цикла компонентов является ключевым для правильного управления состоянием, эффектами и производительностью.


Создание и подключение компонента

Каждый веб-компонент в Haunted создаётся через функцию component, которая принимает функцию рендера и возвращает класс, наследующийся от HTMLElement. При создании экземпляра компонента вызывается конструктор, но рендеринг ещё не происходит.

import { component, html, useState } from 'haunted';

function MyComponent() {
  const [count, setCount] = useState(0);

  return html`
    <div>
      <p>Счёт: ${count}</p>
      <button @click=${() => setCount(count + 1)}>Увеличить</button>
    </div>
  `;
}

customElements.define('my-component', component(MyComponent));

Особенности этапа создания:

  • Конструктор вызывается один раз при создании элемента.
  • Атрибуты ещё не проинициализированы.
  • Хуки можно безопасно использовать внутри функции рендера.

Подключение к DOM (connectedCallback)

Метод connectedCallback вызывается каждый раз, когда компонент вставляется в DOM. В Haunted этот этап управляется автоматически, но важно понимать его для работы с эффектами и подписками.

function MyComponent() {
  useEffect(() => {
    console.log('Компонент добавлен в DOM');

    return () => console.log('Компонент удалён из DOM');
  }, []);
  
  return html`<p>Пример</p>`;
}

Ключевые моменты connectedCallback:

  • Инициализация подписок на события и внешние ресурсы.
  • Эффекты с пустым массивом зависимостей [] выполняются один раз при монтировании.
  • Возврат функции из useEffect позволяет корректно очищать ресурсы при удалении компонента.

Обновление компонента (update)

Обновление происходит при изменении состояния через хуки useState или useReducer. Haunted повторно вызывает функцию рендера и дифференцирует DOM, обновляя только изменившиеся части.

function Counter() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    console.log(`Счёт обновился: ${count}`);
  }, [count]);

  return html`
    <div>
      <p>Счёт: ${count}</p>
      <button @click=${() => setCount(count + 1)}>+</button>
    </div>
  `;
}

Особенности обновления:

  • Хуки с зависимостями вызываются только при изменении этих зависимостей.
  • Haunted минимизирует количество операций с DOM через виртуальный диффинг.
  • Повторный вызов рендера не создаёт новый экземпляр компонента, а обновляет существующий.

Отключение от DOM (disconnectedCallback)

Метод disconnectedCallback вызывается при удалении компонента из DOM. В Haunted он управляет очисткой эффектов и подписок, возвращённых функциями из useEffect.

useEffect(() => {
  const interval = setInterval(() => console.log('Тик'), 1000);

  return () => clearInterval(interval); // Очистка при удалении компонента
}, []);

Особенности отключения:

  • Все эффекты с пустым массивом зависимостей очищаются автоматически.
  • Подписки на события и таймеры должны быть удалены для предотвращения утечек памяти.
  • Метод disconnectedCallback может вызываться несколько раз, если элемент многократно вставляется и удаляется.

Работа с атрибутами и свойствами

Haunted позволяет связывать атрибуты элемента с состоянием через хуки или напрямую через рендер:

function MyComponent({ name }) {
  return html`<p>Привет, ${name}</p>`;
}
  • Атрибуты доступны при рендеринге через объект props.
  • Изменение атрибута вызывает обновление компонента.
  • Хуки useEffect можно использовать для реакции на изменения конкретных пропсов.

Управление побочными эффектами

Эффекты в Haunted являются основным инструментом управления жизненным циклом. useEffect поддерживает:

  • Выполнение при монтировании ([]).
  • Выполнение при изменении зависимостей ([dep1, dep2]).
  • Очистку ресурсов при размонтировании.

Для частых эффектов с асинхронными операциями рекомендуется использовать комбинацию useEffect и AbortController или clearTimeout для предотвращения гонок и утечек.


Оптимизация производительности

Haunted использует виртуальный DOM и повторный рендер только изменившихся частей. Для оптимизации:

  • Избегать ненужных обновлений состояния.
  • Разделять компоненты на мелкие функциональные единицы.
  • Использовать зависимости в useEffect корректно, чтобы эффекты не вызывались лишний раз.

Итоговые принципы жизненного цикла

  1. Создание: конструктор → рендер функции.
  2. Подключение: connectedCallback → выполнение эффектов.
  3. Обновление: изменение состояния → повторный рендер.
  4. Отключение: disconnectedCallback → очистка эффектов и подписок.
  5. Атрибуты и пропсы: инициируют обновления компонента при изменении.
  6. Эффекты: основа для работы с внешними ресурсами и побочными действиями.

Эти механизмы позволяют строить управляемые, производительные и легко поддерживаемые веб-компоненты, полностью интегрированные с современными подходами функционального программирования.