Отражение свойств в атрибуты

SkateJS предоставляет мощный механизм для создания веб-компонентов с декларативным управлением свойствами и их синхронизацией с DOM-атрибутами. Ключевой особенностью является реактивная привязка свойств к атрибутам, которая позволяет автоматически обновлять отображение компонента при изменении данных и наоборот — отражать изменения атрибутов в свойствах.

Свойства и их отражение

В SkateJS свойства компонента определяются в объекте props. Каждое свойство может иметь тип, значение по умолчанию, а также флаг reflect, который отвечает за автоматическое отражение значения свойства в атрибут DOM-элемента.

Пример:

import { define, props, Component } from 'skatejs';

class MyElement extends Component {
  static props = {
    name: props.string({ reflect: true }),
    age: props.number()
  };

  render() {
    return <div>{this.name} — {this.age} лет</div>;
  }
}

define('my-element', MyElement);

В этом примере свойство name имеет reflect: true. Это означает, что любое изменение this.name автоматически обновит соответствующий атрибут name в DOM:

const el = document.querySelector('my-element');
el.name = 'Алексей';
console.log(el.getAttribute('name')); // "Алексей"

При этом свойство age не отражается в атрибуты, так как флаг reflect отсутствует.

Типизация и преобразование значений

SkateJS поддерживает типизацию свойств через встроенные типы: string, number, boolean, array, object. Типы играют важную роль при отражении значений в атрибуты:

  • string — значение передается напрямую как строка.
  • number — значение преобразуется в строку при установке атрибута и обратно в число при чтении.
  • boolean — наличие атрибута означает true, отсутствие — false. Отражение boolean-свойства в атрибут происходит автоматически.

Пример с boolean:

class ToggleButton extends Component {
  static props = {
    active: props.boolean({ reflect: true })
  };

  toggle() {
    this.active = !this.active;
  }
}

define('toggle-button', ToggleButton);

При вызове toggle() значение active изменится, а атрибут active будет добавлен или удален из DOM, обеспечивая синхронизацию состояния компонента и его атрибутов.

Пользовательские преобразователи

SkateJS позволяет использовать функцию attribute для точного контроля отображения свойства в атрибут. Это полезно, если нужно кастомное преобразование, например, при работе с объектами или массивами.

class CustomElement extends Component {
  static props = {
    data: props.string({
      attribute: (value) => JSON.stringify(value),
      reflect: true
    })
  };
}

define('custom-element', CustomElement);

const el = document.querySelector('custom-element');
el.data = { x: 10, y: 20 };
console.log(el.getAttribute('data')); // '{"x":10,"y":20}'

Обновление свойств через атрибуты

Отражение работает в обе стороны. Если атрибут изменяется напрямую в DOM, SkateJS автоматически обновляет соответствующее свойство:

el.setAttribute('name', 'Мария');
console.log(el.name); // "Мария"

Это обеспечивает двустороннюю реактивность: изменения через JavaScript и через DOM остаются синхронизированными.

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

Свойства с отражением можно создавать динамически через объект props. Это особенно полезно при создании компонентов с переменным набором атрибутов:

class DynamicProps extends Component {
  static props = {
    color: props.string({ reflect: true }),
    size: props.number({ reflect: true })
  };
}

define('dynamic-props', DynamicProps);

const el = document.querySelector('dynamic-props');
el.color = 'red';
el.size = 42;

console.log(el.getAttribute('color')); // "red"
console.log(el.getAttribute('size'));  // "42"

Особенности работы с reflect

  • Автоматическое удаление атрибута: если свойство принимает null или undefined, атрибут удаляется из DOM.
  • Отражение boolean: true → атрибут присутствует, false → атрибут отсутствует.
  • Производительность: SkateJS оптимизирует обновления, чтобы не производить лишние изменения DOM при одинаковых значениях.

Практические рекомендации

  • Использовать reflect: true только для свойств, которые должны быть доступны через DOM-атрибуты.
  • Для сложных структур (объекты, массивы) использовать кастомные функции преобразования.
  • При работе с boolean-свойствами опираться на наличие/отсутствие атрибута, чтобы упростить интеграцию с CSS и другими библиотеками.

Итог

Механизм отражения свойств в атрибуты в SkateJS обеспечивает сильную связь между внутренним состоянием компонента и его представлением в DOM. Это облегчает синхронизацию данных, интеграцию с HTML и управляемость компонентов. Ключевые элементы — reflect, типы свойств и возможность кастомного преобразования через attribute.

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