Коммуникация через события

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

Создание и распространение событий

Для генерации события в SkateJS используется стандартный объект CustomEvent. Важным аспектом является правильная конфигурация события:

  • bubbles: определяет, будет ли событие всплывать вверх по дереву DOM.
  • composed: разрешает прохождение события через Shadow DOM.
  • detail: объект, содержащий данные события.

Пример создания события:

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

class MyButton extends Component {
  static props = {
    label: props.string
  };

  handleClick() {
    this.dispatchEvent(new CustomEvent('button-click', {
      bubbles: true,
      composed: true,
      detail: { message: 'Кнопка нажата' }
    }));
  }

  renderCallback() {
    return `<button oncl ick="${() => this.handleClick()}">${this.label}</button>`;
  }
}

define('my-button', MyButton);

Здесь событие button-click распространяется вверх по DOM и может быть перехвачено родительским элементом или любым другим слушателем.

Подписка на события

Подписка на события в SkateJS может выполняться двумя способами: через HTML-атрибуты или через JavaScript-слушатели.

HTML-атрибуты

Можно задать обработчик события прямо в разметке компонента:

<my-button label="Нажми меня" onbutton-click="handleButtonClick(event)"></my-button>
<script>
  function handleButtonClick(event) {
    console.log(event.detail.message); // Кнопка нажата
  }
</script>

JavaScript

События чаще всего обрабатываются программно:

const button = document.querySelector('my-button');
button.addEventListener('button-click', event => {
  console.log(event.detail.message);
});

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

Кастомные события с типизацией

SkateJS поддерживает передачу структурированных данных через объект detail. Это особенно важно при создании сложных интерфейсов с взаимодействием компонентов.

this.dispatchEvent(new CustomEvent('form-submit', {
  bubbles: true,
  composed: true,
  detail: {
    values: { name: 'Иван', age: 30 },
    valid: true
  }
}));

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

События жизненного цикла компонентов

SkateJS предоставляет встроенные события и методы жизненного цикла, которые можно использовать для управления состоянием:

  • connectedCallback: вызывается при подключении компонента к DOM.
  • disconnectedCallback: вызывается при удалении компонента из DOM.
  • attributeChangedCallback: реагирует на изменения свойств компонента.

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

class MyInput extends Component {
  static props = {
    value: props.string
  };

  connectedCallback() {
    super.connectedCallback();
    this.addEventListener('input', this.handleInput.bind(this));
  }

  handleInput(event) {
    this.value = event.target.value;
    this.dispatchEvent(new CustomEvent('value-changed', {
      bubbles: true,
      composed: true,
      detail: { value: this.value }
    }));
  }
}

Всплытие и композиция событий

Ключевой момент при проектировании коммуникации через события — правильное использование свойств bubbles и composed. Без них события могут не достигать родительских компонентов в Shadow DOM, что ограничивает взаимодействие.

  • bubbles: true — событие будет всплывать по дереву DOM.
  • composed: true — событие может пройти через границы Shadow DOM.

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

Передача данных между компонентами

Часто требуется передать данные из одного компонента в другой через событие. Это делается с помощью CustomEvent.detail и подписки на него в родителе:

class ParentComponent extends Component {
  connectedCallback() {
    super.connectedCallback();
    this.addEventListener('child-update', event => {
      console.log('Данные от дочернего компонента:', event.detail);
    });
  }

  renderCallback() {
    return `<child-component></child-component>`;
  }
}

define('parent-component', ParentComponent);

В дочернем компоненте событие создаётся с данными:

this.dispatchEvent(new CustomEvent('child-update', {
  bubbles: true,
  composed: true,
  detail: { status: 'готово' }
}));

События и реактивные свойства

SkateJS позволяет синхронизировать свойства компонентов с событиями, создавая реактивное поведение. Например, изменение значения свойства может автоматически вызывать событие:

static props = {
  count: props.number({
    attribute: true,
    set(value, oldValue) {
      if (value !== oldValue) {
        this.dispatchEvent(new CustomEvent('count-changed', {
          bubbles: true,
          composed: true,
          detail: { count: value }
        }));
      }
      return value;
    }
  })
};

Таким образом достигается двусторонняя связь между компонентами через стандартные события.

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

  • Всегда использовать bubbles: true и composed: true для событий, которые должны взаимодействовать с родителями через Shadow DOM.
  • Для передачи данных применять CustomEvent.detail вместо глобальных состояний.
  • Реализовывать обработку событий через addEventListener, а не только через HTML-атрибуты, для лучшей масштабируемости.
  • Комбинировать события жизненного цикла компонента с пользовательскими событиями для реактивной логики.

Эти принципы создают чёткую и предсказуемую коммуникацию компонентов, сохраняя инкапсуляцию и совместимость с DOM-стандартами.