mcpField
S
stimulus

stimulus

github.com/smnandre开发18292 次安装20 周前更新
代码仓库

关于 stimulus

用于使用 Stimulus 的技能。Stimulus 通过 data 属性为现有 HTML 添加行为,并涵盖控制器绑定、类型化值、动作、跨控制器 outlet 和延迟加载,以 HTML 为事实来源。

主要功能

  1. 01通过 data 属性绑定控制器
  2. 02将事件关联到控制器动作
  3. 03使用类型化值与 targets
  4. 04通过 outlets 连接控制器
  5. 05延迟加载控制器依赖
  6. 06与 Symfony 和 Twig 集成

适用场景

  1. 01为服务端渲染的 HTML 添加行为
  2. 02处理按钮、表单和输入事件
  3. 03协调相互关联的控制器实例
  4. 04仅在元素可见时加载重型依赖

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/smnandre/symfony-ux-skills --skill stimulus

安装后在 Claude Code 中输入/stimulus确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
api.md
gotchas.md
patterns.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-* 属性。

参考资料

另请参阅

  • UX Map 会在地图容器元素上分发兼容 Stimulus 的事件(ux:map:connect、ux:map:marker:after-create 等)。使用自定义 Stimulus 控制器扩展地图行为。
  • UX Icons 是纯 SVG,无需集成 Stimulus;但你可以使用 Stimulus 通过值变更动态切换图标名称。

评论

支持 Markdown