Наблюдатель для реактивности

Stimulus — это легковесный JavaScript-фреймворк, ориентированный на улучшение интерактивности HTML через минимальные вмешательства в разметку. Основной строительный блок — контроллер, который привязывается к DOM-элементам через data-controller. Важная особенность Stimulus — реактивность, достигаемая через наблюдатели, которые автоматически реагируют на изменения состояния или DOM.

Наблюдатель в Stimulus — это механизм, позволяющий контроллеру отслеживать изменения в DOM, атрибутах или значениях свойств и автоматически выполнять связанные действия. Реактивность обеспечивается через связывание элементов с методами контроллера, а также через использование специальных атрибутов data-<controller>-target и data-<controller>-action.


Targets: точка привязки данных

Targets — это элементы внутри контроллера, которые можно динамически изменять или отслеживать. Они задаются через атрибут data-<controller>-target="имя".

Пример:

<div data-controller="counter">
  <span data-counter-target="output">0</span>
  <button data-action="click->counter#increment">+</button>
</div>

В контроллере counter:

import { Controller } from "@hotwired/stimulus";

export default class extends Controller {
  static targets = ["output"];
  increment() {
    this.outputTarget.textContent = parseInt(this.outputTarget.textContent) + 1;
  }
}

Ключевой момент: любое изменение через target автоматически отражается на DOM-элементе, с которым он связан, что обеспечивает базовую реактивность.


Value-объекты для реактивности

Stimulus позволяет создавать Value-объекты, которые отслеживают изменения данных и вызывают соответствующие методы при их обновлении. Для этого в контроллере объявляются свойства через static values.

Пример:

export default class extends Controller {
  static values = { count: Number }

  connect() {
    console.log(this.countValue); // значение по умолчанию
  }

  increment() {
    this.countValue++;
  }

  countValueChanged() {
    console.log(`Новое значение: ${this.countValue}`);
  }
}
  • countValue — текущее значение свойства.
  • countValueChanged — метод автоматически вызывается при изменении значения.

Особенность: Stimulus автоматически связывает value с DOM, если используется атрибут data-<controller>-count-value.

<div data-controller="counter" data-counter-count-value="0">
  <span data-counter-target="output"></span>
</div>

Actions: связывание событий с методами

События DOM привязываются к методам контроллера через атрибут data-action. Это обеспечивает реактивную обработку пользовательских действий без прямой манипуляции DOM.

Синтаксис:

<button data-action="click->controller#method">Click</button>
  • click — событие DOM.
  • controller#method — метод контроллера, который будет вызван.
  • Можно цепочкой добавлять несколько действий на одно событие.

Stimulus обрабатывает события централизованно, что повышает читаемость кода и упрощает тестирование.


Observers: реактивность на уровне DOM

Для сложных сценариев, когда требуется следить за изменениями атрибутов или дочерних элементов, используются MutationObserver или сторонние библиотеки, интегрируемые с Stimulus.

Пример использования MutationObserver внутри контроллера:

export default class extends Controller {
  connect() {
    this.observer = new MutationObserver(this.onMutation.bind(this));
    this.observer.observe(this.element, { childList: true, subtree: true });
  }

  onMutation(mutations) {
    mutations.forEach(mutation => {
      console.log('Изменение DOM:', mutation);
    });
  }

  disconnect() {
    this.observer.disconnect();
  }
}

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


Реактивные комбинации: targets + values + actions

Stimulus позволяет строить реактивные интерфейсы, комбинируя Targets, Values и Actions. Такой подход обеспечивает:

  1. Автоматическое обновление DOM при изменении значений.
  2. Простую привязку событий к методам.
  3. Минимум ручной логики по синхронизации состояния и интерфейса.

Пример полноценного счетчика:

<div data-controller="counter" data-counter-count-value="0">
  <span data-counter-target="output"></span>
  <button data-action="click->counter#increment">+</button>
  <button data-action="click->counter#decrement">-</button>
</div>

Контроллер:

export default class extends Controller {
  static targets = ["output"];
  static values = { count: Number }

  connect() {
    this.updateOutput();
  }

  increment() {
    this.countValue++;
  }

  decrement() {
    this.countValue--;
  }

  countValueChanged() {
    this.updateOutput();
  }

  updateOutput() {
    this.outputTarget.textContent = this.countValue;
  }
}

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


Рекомендации по использованию наблюдателя

  • Использовать targets для элементов, которые часто изменяются.
  • Values подходят для реактивного хранения состояния.
  • Actions связывают события с методами контроллера без дублирования кода.
  • MutationObserver — для динамического контента или сложных структур DOM.
  • Минимизировать вложенность наблюдателей, чтобы не создавать лишнюю нагрузку на браузер.

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