关于 stimulus
用于使用 Stimulus 的技能。Stimulus 通过 data 属性为现有 HTML 添加行为,并涵盖控制器绑定、类型化值、动作、跨控制器 outlet 和延迟加载,以 HTML 为事实来源。
主要功能
- 01通过 data 属性绑定控制器
- 02将事件关联到控制器动作
- 03使用类型化值与 targets
- 04通过 outlets 连接控制器
- 05延迟加载控制器依赖
- 06与 Symfony 和 Twig 集成
适用场景
- 01为服务端渲染的 HTML 添加行为
- 02处理按钮、表单和输入事件
- 03协调相互关联的控制器实例
- 04仅在元素可见时加载重型依赖
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ smnandre/ symfony-ux-skills --skill stimulus安装后在 Claude Code 中输入/stimulus确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/api.mdgotchas.mdpatterns.md详细介绍
Stimulus
简洁的 JavaScript 框架,通过数据属性将 JS 对象连接到 HTML。Stimulus 不会渲染 HTML,而是为服务器渲染的 HTML 添加行为。
其思维模型是:HTML 是事实来源,JavaScript 控制器附加到元素上,数据属性负责连接。使用 AssetMapper 时无需构建步骤。
快速参考
data-controller="name" attach controller to element
data-name-target="item" mark element as a target
data-action="event->name#method" bind event to controller method
data-name-key-value="..." pass typed data to controller
data-name-key-class="..." configure CSS class names
data-name-other-outlet=".selector" reference another controller instance
控制器骨架
// assets/controllers/example_controller.js
import { Controller } from '@hotwired/stimulus';
export default class extends Controller {
static targets = ['input', 'output'];
static values = { url: String, delay: { type: Number, default: 300 } };
static classes = ['loading'];
static outlets = ['other'];
connect() {
// Called when controller connects to DOM
}
disconnect() {
// Called when controller disconnects -- clean up here
}
submit(event) {
// Action method
}
}
文件命名约定:hello_controller.js 对应 data-controller="hello"。子目录使用 -- 作为分隔符:components/modal_controller.js 对应 data-controller="components--modal"。
HTML 连接示例
基础控制器
<div data-controller="hello">
<input data-hello-target="name" type="text">
<button data-action="click->hello#greet">Greet</button>
<span data-hello-target="output"></span>
</div>
来自服务器的值(Twig)
通过值属性将服务器数据传递给控制器。值有类型,并会自动解析。
<div data-controller="map"
data-map-latitude-value="{{ place.lat }}"
data-map-longitude-value="{{ place.lng }}"
data-map-zoom-value="12">
</div>
可用类型:String、Number、Boolean、Array、Object。值发生变化时会触发 {name}ValueChanged() 回调。
操作
格式为 event->controller#method。每种元素类型都有默认事件(按钮的 click、输入框的 input、表单的 submit),因此可以省略事件。
{# Explicit event #}
<button data-action="click->hello#greet">Greet</button>
{# Default event (click for button) #}
<button data-action="hello#greet">Greet</button>
{# Multiple actions on same element #}
<input type="text"
data-action="focus->field#highlight blur->field#normalize input->field#validate">
{# Prevent default #}
<form data-action="submit->form#validate:prevent">
{# Keyboard shortcuts #}
<div data-action="keydown.esc@window->modal#close">
<input data-action="keydown.enter->modal#submit keydown.ctrl+s->modal#save">
{# Global events (window/document) #}
<div data-action="resize@window->sidebar#adjust click@document->sidebar#closeOutside">
CSS 类
将 CSS 类名外置,让控制器保持通用:
<button data-controller="button"
data-button-loading-class="opacity-50 cursor-wait"
data-button-active-class="bg-blue-600"
data-action="click->button#submit">
Submit
</button>
// In controller
this.element.classList.add(...this.loadingClasses);
多个控制器
一个元素可以有多个控制器:
<div data-controller="dropdown tooltip"
data-action="mouseenter->tooltip#show mouseleave->tooltip#hide">
<button data-action="click->dropdown#toggle">Menu</button>
<ul data-dropdown-target="menu" hidden>...</ul>
</div>
Outlets(跨控制器通信)
通过 CSS 选择器引用其他控制器实例:
<div data-controller="player"
data-player-playlist-outlet="#playlist">
<button data-action="click->player#playNext">Next</button>
</div>
<ul id="playlist" data-controller="playlist">
<li data-playlist-target="track">Song 1</li>
<li data-playlist-target="track">Song 2</li>
</ul>
// In player controller
static outlets = ['playlist'];
playNext() {
const tracks = this.playlistOutlet.trackTargets;
// ...
}
延迟加载(大型依赖)
仅当元素进入视口时才加载控制器 JS。适用于依赖较大型库的控制器(图表库、编辑器、地图)。
/* stimulusFetch: 'lazy' */
import { Controller } from '@hotwired/stimulus';
import Chart from 'chart.js';
export default class extends Controller {
connect() {
// Chart.js is only loaded when this element enters the viewport
}
}
/* stimulusFetch: 'lazy' */ 注释必须位于文件第一行。
Symfony / Twig 集成
建议直接使用原始数据属性——它们适用于所有场景、易于阅读,也无需特殊辅助函数。
{# Raw attributes (preferred) #}
<div data-controller="search"
data-search-url-value="{{ path('api_search') }}">
对于复杂场景或需要以编程方式生成属性的情况,可以使用 Twig 辅助函数:
{# Twig helper #}
<div {{ stimulus_controller('search', { url: path('api_search') }) }}>
{# Chaining multiple controllers #}
<div {{ stimulus_controller('a')|stimulus_controller('b') }}>
{# Target and action helpers #}
<input {{ stimulus_target('search', 'query') }}>
<button {{ stimulus_action('search', 'submit') }}>
核心原则
HTML 驱动,JS 响应。 控制器不会创建标记,而是为现有 HTML 添加行为。如果你发现自己在控制器中生成 DOM,不妨考虑 TwigComponent 或 LiveComponent 是否更合适。
一个控制器,一个职责。 下拉菜单控制器负责处理下拉菜单,工具提示控制器负责处理工具提示。与其构建庞大的控制器,不如在同一元素上组合多个控制器。
在 disconnect() 中清理。 如果 connect() 添加了事件监听器、计时器或第三方库实例,disconnect() 必须将其移除。页面切换时,Turbo 导航会断开并重新连接控制器。
优先使用值,而非数据属性。 对于控制器需要读取或监视的数据,应使用 Stimulus 值(有类型,并带有变更回调),而不是原始的 data-* 属性。
参考资料
- 完整 API(生命周期、目标、值、操作、类、outlets):references/api.md
- 模式(防抖、fetch、模态框、表单等):references/patterns.md
- 常见陷阱(常见错误、调试、Turbo 兼容性):references/gotchas.md
另请参阅
- UX Map 会在地图容器元素上分发兼容 Stimulus 的事件(
ux:map:connect、ux:map:marker:after-create等)。使用自定义 Stimulus 控制器扩展地图行为。 - UX Icons 是纯 SVG,无需集成 Stimulus;但你可以使用 Stimulus 通过值变更动态切换图标名称。