Типы свойств

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


Определение свойств

В Haunted свойства компонента задаются через объект observedAttributes или через декоратор @property (если используется TypeScript и соответствующие расширения). Основная цель — связать внешние атрибуты элемента с внутренним состоянием компонента.

import { component, html } from 'haunted';

function MyComponent({ title }) {
  return html`<h1>${title}</h1>`;
}

customElements.define('my-component', component(MyComponent, { observedAttributes: ['title'] }));

Здесь title — это свойство компонента, которое автоматически отражается в DOM как атрибут и может передаваться при использовании элемента: <my-component title="Пример"></my-component>.


Типы свойств

Haunted не накладывает жёстких ограничений на типы свойств, однако важно понимать различие между строковыми атрибутами HTML и типами JavaScript, которые фактически используются внутри компонента.

  1. Строковые свойства Атрибуты HTML всегда передаются как строки. Haunted автоматически преобразует значение атрибута в строку, если не используется кастомное преобразование. Это удобно для текстовых данных:

    function TextComponent({ text }) {
      return html`<p>${text}</p>`;
    }
    
    customElements.define('text-component', component(TextComponent, { observedAttributes: ['text'] }));

    Здесь <text-component text="Привет"></text-component> передаст значение "Привет" в свойство text.

  2. Булевы свойства Булевы значения в HTML задаются особым образом: наличие атрибута означает true, а отсутствие — false. В Haunted это можно обработать с помощью функции-преобразователя:

    function Toggle({ active }) {
      return html`<div class="${active ? 'on' : 'off'}"></div>`;
    }
    
    customElements.define('toggle-switch', component(Toggle, {
      observedAttributes: {
        active: {
          converter: (value) => value !== null
        }
      }
    }));

    Теперь <toggle-switch active></toggle-switch> создаст включённый элемент, а <toggle-switch></toggle-switch> — выключенный.

  3. Числовые свойства Для чисел необходимо явно конвертировать строку из атрибута в число, иначе Haunted будет использовать строку. Обычно используется Number() или parseInt/parseFloat:

    function Counter({ count }) {
      return html`<p>Счёт: ${count}</p>`;
    }
    
    customElements.define('counter-element', component(Counter, {
      observedAttributes: {
        count: {
          converter: (value) => Number(value)
        }
      }
    }));

    Теперь <counter-element count="42"></counter-element> корректно передаст число 42 в свойство count.

  4. Объектные и массивные свойства Так как HTML-атрибуты — строки, объекты и массивы нужно передавать через сериализацию, например, в JSON:

    function List({ items }) {
      return html`<ul>${items.map(item => html`<li>${item}</li>`)}</ul>`;
    }
    
    customElements.define('list-component', component(List, {
      observedAttributes: {
        items: {
          converter: (value) => JSON.parse(value)
        }
      }
    }));

    Использование: <list-component items='["яблоко","банан"]'></list-component>.


Динамическое обновление свойств

Haunted автоматически перерисовывает компонент при изменении свойств. Любое изменение в объекте observedAttributes вызывает повторный рендер.

const el = document.querySelector('counter-element');
el.count = 10; // автоматически обновится отображение

Важно: если свойство является объектом или массивом, Haunted отслеживает только изменение ссылки, а не внутренние мутации. Для перерисовки нужно присвоить новое значение:

el.items = [...el.items, 'груша'];

Сопоставление атрибутов и свойств

Haunted позволяет разделять внутренние свойства и атрибуты HTML. Атрибут может существовать, но не обязательно отображать сложные внутренние состояния. Рекомендуется придерживаться следующих правил:

  • Атрибуты — для публичного интерфейса и простых типов: строки, числа, булевы значения.
  • Свойства — для внутренних объектов, массивов и функций, которые не нужно отображать в HTML.

Пример смешанного подхода:

function UserCard({ name, age, onClick }) {
  return html`<div @click=${onClick}>${name}, ${age} лет</div>`;
}

customElements.define('user-card', component(UserCard, {
  observedAttributes: ['name', 'age']
}));

const card = document.createElement('user-card');
card.name = 'Иван';
card.age = 30;
card.onCl ick = () => alert('Клик!');
document.body.appendChild(card);

Свойства с дефолтными значениями

Haunted поддерживает установку значений по умолчанию через деструктуризацию аргументов функции компонента:

function Badge({ label = 'Новый', color = 'blue' }) {
  return html`<span style="color:${color}">${label}</span>`;
}

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


Итоговые рекомендации по типам свойств

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

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