Расширения браузера

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

Основная цель Haunted — сделать веб-компоненты легковесными, реактивными и модульными, сохраняя преимущества стандартов браузера без дополнительного виртуального DOM.


Установка и подключение

Haunted можно подключить несколькими способами:

  1. Через npm:
npm install haunted
  1. Через CDN:
<script type="module">
  import { html, component, useState, useEffect } from 'https://unpkg.com/haunted/haunted.js?module';
</script>

После подключения доступны следующие ключевые функции: html, component, useState, useEffect, useRef, useMemo.


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

Для создания компонента используется функция component, которая превращает обычную функцию в кастомный элемент:

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

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

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

customElements.define('my-counter', component(Counter));

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

  • html — теговая функция шаблонных литералов для рендеринга HTML.
  • useState — хук состояния, аналог React useState.
  • @click — синтаксис Haunted для привязки событий (делегирует события нативным элементам).

Реактивность и хуки

Haunted поддерживает основные хуки, позволяющие управлять состоянием и жизненным циклом компонента:

  1. useState — реактивное состояние:
const [value, setValue] = useState('текст');
  1. useEffect — аналог жизненного цикла componentDidMount, componentDidUpdate и componentWillUnmount:
useEffect(() => {
  console.log('Компонент отрендерился');

  return () => {
    console.log('Компонент размонтирован');
  };
}, [value]); // зависимость
  1. useRef — ссылка на DOM-элемент:
const inputRef = useRef();
  1. useMemo — кэширование вычислений:
const computedValue = useMemo(() => expensiveCalculation(value), [value]);

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


Передача данных через свойства

Haunted использует props для передачи данных компонентам. Свойства можно задать при создании элемента в DOM:

<my-counter initial="10"></my-counter>

В функции компонента свойства доступны как объект:

function Counter({ initial }) {
  const [count, setCount] = useState(Number(initial) || 0);
}

Важно учитывать, что все значения из атрибутов приходят как строки, поэтому требуется явное преобразование типов.


Работа с событиями

События можно привязывать прямо через шаблон:

html`<button @click=${handleClick}>Нажми меня</button>`

При этом функция handleClick будет вызвана с объектом события Event. Такой подход позволяет избегать ручного поиска элементов через querySelector.


Стилизация компонентов

Haunted поддерживает Shadow DOM, что позволяет инкапсулировать стили:

function StyledButton() {
  return html`
    <style>
      button {
        background: #007acc;
        color: white;
        border: none;
        padding: 10px 20px;
        cursor: pointer;
      }
      button:hover {
        background: #005fa3;
      }
    </style>
    <button>Кнопка</button>
  `;
}

customElements.define('styled-button', component(StyledButton));

При использовании Shadow DOM стили компонента не влияют на глобальные стили страницы и наоборот.


Подключение к расширениям браузера

Haunted идеально подходит для разработки расширений браузера, так как позволяет создавать легковесные и изолированные UI-компоненты:

  • Popup и панели — компоненты с собственным Shadow DOM.
  • Контент-скрипты — внедрение кастомных элементов на сторонние страницы без конфликтов с существующими стилями.
  • Обработка состояния — через хуки легко управлять реактивными данными внутри расширения.

Пример использования Haunted в popup расширения Chrome:

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

function Popup() {
  const [enabled, setEnabled] = useState(false);

  return html`
    <div>
      <h2>Расширение включено: ${enabled}</h2>
      <button @click=${() => setEnabled(!enabled)}>
        ${enabled ? 'Выключить' : 'Включить'}
      </button>
    </div>
  `;
}

customElements.define('extension-popup', component(Popup));

Этот компонент полностью автономен и может быть встроен в popup.html или content script.


Работа с локальным хранилищем

Для расширений часто требуется хранение состояния между сессиями. Haunted позволяет комбинировать хуки с localStorage или API браузера:

function Toggle() {
  const [enabled, setEnabled] = useState(() => {
    return JSON.parse(localStorage.getItem('enabled')) || false;
  });

  useEffect(() => {
    localStorage.setItem('enabled', JSON.stringify(enabled));
  }, [enabled]);

  return html`
    <button @click=${() => setEnabled(!enabled)}>
      ${enabled ? 'Включено' : 'Выключено'}
    </button>
  `;
}

customElements.define('toggle-button', component(Toggle));

Использование useEffect позволяет автоматически синхронизировать состояние с хранилищем при каждом изменении.


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

Haunted минималистична:

  • Нет виртуального DOM, используется native DOM diffing.
  • Поддержка Shadow DOM уменьшает перерисовку глобальных стилей.
  • Реактивность на уровне хуков позволяет обновлять только изменившиеся элементы.

Для расширений это критично, так как UI должен быть быстрым и отзывчивым, а нагрузка на браузер минимальной.


Итоговые особенности для расширений

  • Легкие и изолированные компоненты через Shadow DOM.
  • Управление состоянием с хуками (useState, useEffect).
  • Прямое взаимодействие с DOM через useRef.
  • Возможность интеграции с локальным и синхронным хранилищем браузера.
  • Минимальная нагрузка на производительность, что важно для расширений.

Haunted позволяет создавать современные, масштабируемые UI для браузерных расширений, сочетая функциональный стиль React и нативные возможности Web Components.