Stimulus изначально проектировался как минималистичный фреймворк для добавления поведения к уже существующей HTML-разметке. В отличие от компонентных SPA-фреймворков, здесь компонент — это не виртуальное дерево и не шаблон, а связка HTML + контроллер + соглашения об именовании. Переиспользуемость достигается не за счёт абстрактных фабрик, а через композицию, параметры и чёткие границы ответственности.
Переиспользуемый компонент в Stimulus — это:
data-* атрибуты;Минимальный компонент состоит из HTML-узла и контроллера:
<div data-controller="counter">
<span data-counter-target="value">0</span>
<button data-action="counter#increment">+</button>
</div>
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = ["value"]
increment() {
this.valueTarget.textContent =
parseInt(this.valueTarget.textContent, 10) + 1
}
}
Для переиспользуемости важно, чтобы:
targets,
values и classes;this.element).Stimulus предоставляет механизм values для передачи
данных из HTML в контроллер. Это основной инструмент настройки
компонентов без изменения кода.
<div
data-controller="counter"
data-counter-start-value="10"
data-counter-step-value="5"
>
<span data-counter-target="value"></span>
<button data-action="counter#increment">+</button>
</div>
export default class extends Controller {
static targets = ["value"]
static values = {
start: Number,
step: Number
}
connect() {
this.valueTarget.textContent = this.startValue
}
increment() {
this.valueTarget.textContent =
parseInt(this.valueTarget.textContent, 10) + this.stepValue
}
}
Ключевые свойства values:
Компонент становится полностью настраиваемым декларативно, без изменения JavaScript.
Переиспользуемый компонент не должен:
document напрямую;this.element;Плохая практика:
document.querySelector(".modal").classList.add("open")
Корректный подход:
this.element.classList.add("open")
Если требуется взаимодействие между компонентами, используется событийная модель.
Stimulus поощряет слабую связанность. Один компонент может генерировать событие, другой — реагировать на него.
this.element.dispatchEvent(
new CustomEvent("counter:changed", {
detail: { value: newValue },
bubbles: true
})
)
<div
data-controller="logger"
data-action="counter:changed->logger#log"
></div>
export default class extends Controller {
log(event) {
console.log(event.detail.value)
}
}
Компоненты остаются независимыми и могут использоваться в любом сочетании.
Stimulus не навязывает иерархию компонентов. Один DOM-элемент может иметь несколько контроллеров:
<div
data-controller="dropdown analytics"
data-action="click->dropdown#toggle"
>
Каждый контроллер отвечает за свою задачу:
dropdown — управление состоянием;analytics — сбор событий.Композиция позволяет:
Для работы с CSS-классами используется API classes,
исключающее жёсткое кодирование имён классов.
<div
data-controller="toggle"
data-toggle-active-class="is-active"
>
export default class extends Controller {
static classes = ["active"]
toggle() {
this.element.classList.toggle(this.activeClass)
}
}
Преимущества:
Для часто повторяющихся паттернов (модальные окна, раскрывающиеся блоки, вкладки) важно избегать копирования логики. Stimulus-контроллеры легко масштабируются на любое количество экземпляров.
<div data-controller="accordion">
<button data-action="accordion#toggle">Заголовок</button>
<div data-accordion-target="panel"></div>
</div>
<div data-controller="accordion">
<button data-action="accordion#toggle">Другой заголовок</button>
<div data-accordion-target="panel"></div>
</div>
Один контроллер управляет любым количеством компонентов без дополнительных условий.
Для более сложных сценариев допускается наследование:
export default class extends BaseDropdownController {
open() {
super.open()
this.trackOpen()
}
}
Наследование оправдано, когда:
В большинстве случаев предпочтительнее композиция, так как она сохраняет гибкость.
Переиспользуемость невозможна без соглашений:
toggle, а не headerMenuToggle);Хорошая структура каталогов:
controllers/
toggle_controller.js
modal_controller.js
dropdown_controller.js
Контроллеры не группируются по страницам, а по функциональности.
Изолированный контроллер легко тестировать:
Пример подхода:
Это напрямую вытекает из принципов переиспользуемости.
Переиспользуемый компонент в Stimulus — это:
Такая модель позволяет внедрять интерактивность постепенно, не переписывая архитектуру и не теряя контроль над сложностью.