Реактивность и отслеживание изменений

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

Реактивность в Stimulus обеспечивается через:

  • Свойства и геттеры/сеттеры контроллера
  • Targets — элементы DOM, на которые контроллер «подписан»
  • Value properties — специальные свойства контроллера, автоматически отслеживающие изменения

Контроллеры и Targets

Контроллер в Stimulus объявляется через класс, который наследуется от Controller:

import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static targets = ["output"]

  connect() {
    console.log("Контроллер подключен")
  }

  updateOutput(value) {
    this.outputTarget.textContent = value
  }
}
  • static targets — массив строк, где каждая строка указывает на элемент в DOM с соответствующим атрибутом data-<controller>-target.
  • this.<targetName>Target — доступ к DOM-элементу, который автоматически «реактивно» связан с контроллером.

Изменения состояния в контроллере можно моментально отражать в DOM через методы контроллера. Stimulus отслеживает привязку к target и гарантирует корректную работу после подключения и отключения контроллера.


Value Properties

Value properties — это механизм для создания реактивных свойств контроллера. Они позволяют автоматически отслеживать изменения значений и выполнять связанные действия.

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

  connect() {
    console.log(`Начальное значение: ${this.countValue}`)
  }

  increment() {
    this.countValue++
  }

  countValueChanged() {
    console.log(`Новое значение: ${this.countValue}`)
  }
}

Особенности:

  • static values — объявление свойств, которые будут автоматически синхронизироваться с атрибутами DOM вида data-<controller>-count-value.
  • <property>ValueChanged — метод, вызываемый автоматически при изменении значения свойства.
  • Обеспечивает автоматическую реактивность, без ручного подписывания на события.

События и отслеживание изменений

Stimulus использует события DOM, чтобы реагировать на действия пользователя. Комбинируя value properties и targets, можно построить реактивные интерфейсы без лишнего кода.

Пример:

<div data-controller="counter" data-counter-count-value="0">
  <span data-counter-target="output"></span>
  <button data-action="click->counter#increment">Добавить</button>
</div>
export default class extends Controller {
  static targets = ["output"]
  static values = { count: Number }

  connect() {
    this.updateOutput()
  }

  increment() {
    this.countValue++
  }

  countValueChanged() {
    this.updateOutput()
  }

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

В этом примере:

  • Кнопка вызывает метод increment.
  • Изменение countValue автоматически вызывает countValueChanged.
  • Метод updateOutput обновляет DOM без явной подписки на изменения.

Поддержка множественных Targets и Value Properties

Stimulus позволяет использовать несколько targets и value properties в одном контроллере, сохраняя реактивность. Например:

export default class extends Controller {
  static targets = ["name", "greeting"]
  static values = { username: String }

  connect() {
    this.updateGreeting()
  }

  usernameValueChanged() {
    this.updateGreeting()
  }

  updateGreeting() {
    this.greetingTarget.textContent = `Привет, ${this.usernameValue}`
  }
}

Использование нескольких targets упрощает управление интерфейсом, а value properties обеспечивают синхронизацию данных с DOM. Любое изменение значения через this.usernameValue мгновенно отражается на целевых элементах.


Реактивность без перегрузки

Stimulus оптимизирован для минимальной сложности:

  • Нет виртуального DOM и диффинга.
  • Реактивность достигается через объекты и методы контроллера.
  • Изменения значений и состояния автоматически отражаются на связанных DOM-элементах через targets и value properties.

Такой подход снижает объем кода и повышает читаемость приложения, сохраняя легковесность и скорость.


Динамическое создание и удаление элементов

Stimulus корректно отслеживает динамически добавленные или удаленные элементы. Если новый элемент содержит data-target, контроллер автоматически его зарегистрирует:

const newDiv = document.createElement("div")
newDiv.dataset.counterTarget = "output"
document.body.appendChild(newDiv)

После добавления DOM Stimulus корректно свяжет newDiv с контроллером, обеспечивая реактивность без дополнительной инициализации.


Применение в сложных интерфейсах

Комбинация targets, value properties, и событий позволяет строить сложные реактивные интерфейсы с минимальной логикой:

  • Фильтры и сортировка списков.
  • Реактивные формы.
  • Слайдеры и прогресс-бары.
  • Управление модальными окнами.

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