mcpField
S
symfony-ux

symfony-ux

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

关于 symfony-ux

Symfony UX 是面向 Symfony 开发者的前端工具集,以服务端渲染 HTML 为基础,按需添加交互。它组合 Stimulus、Turbo、TwigComponent 和 LiveComponent 等工具,涵盖客户端行为、局部更新与响应式组件。

主要功能

  1. 01Stimulus 添加轻量 JavaScript 行为
  2. 02Turbo 支持导航和页面区域更新
  3. 03TwigComponent 构建可复用界面组件
  4. 04LiveComponent 提供服务端响应式交互
  5. 05UX Icons 渲染内联 SVG 图标
  6. 06UX Map 配置交互式地图

适用场景

  1. 01为现有 HTML 添加切换或标签页交互
  2. 02无需整页重载更新分页或模态框
  3. 03构建带实时验证的表单与搜索
  4. 04通过 Turbo Streams 更新多个页面区域

安装

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

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

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

详细介绍

Symfony UX

面向 Symfony 的现代前端技术栈。使用服务器渲染的 HTML,以最少的 JavaScript 构建响应式 UI。

Symfony UX 遵循渐进增强理念:从纯 HTML 开始,仅在需要时添加交互性,并优先使用服务器端渲染,而非客户端 JavaScript。每种工具都用于解决特定问题——选择最简单且适合的工具。

决策树:选择哪种工具?

Need frontend interactivity?
|
+-- Pure JavaScript behavior (no server)?
|   -> Stimulus
|      (DOM manipulation, event handling, third-party libs)
|
+-- Navigation / partial page updates?
|   -> Turbo
|      +-- Full page AJAX        -> Turbo Drive (automatic, zero config)
|      +-- Single section update  -> Turbo Frame
|      +-- Multiple sections      -> Turbo Stream
|
+-- Reusable UI component?
|   |
|   +-- Static (no live updates)?
|   |   -> TwigComponent
|   |      (props, blocks, computed properties)
|   |
|   +-- Dynamic (re-renders on interaction)?
|       -> LiveComponent
|          (data binding, actions, forms, real-time validation)
|
+-- Need icons?
|   -> UX Icons
|      (inline SVG from 200+ Iconify sets or local files)
|
+-- Need an interactive map?
|   -> UX Map
|      (Leaflet or Google Maps, markers, polygons, circles)
|
+-- Real-time (WebSocket/SSE)?
    -> Turbo Stream + Mercure

这些工具可以自然地组合使用。典型页面会使用 Turbo Drive 实现导航,使用 Turbo Frames 处理局部区域,使用 TwigComponents 构建可复用的 UI 元素,使用 LiveComponents 实现响应式表单/搜索,并使用 Stimulus 实现无需服务器往返的客户端行为。

快速对比

特性StimulusTurboTwigComponentLiveComponent
是否需要 JavaScript是(最少量)否否否
服务器重新渲染否是(页面/框架)否是(AJAX)
状态管理仅 JSURL/服务器Props(不可变)LiveProp(可变)
双向绑定手动否否data-model
是否支持实时功能手动是(Streams+Mercure)否是(轮询/emit)
延迟加载是(stimulusFetch)是(延迟加载 frames)否是(defer/lazy)

UX Icons 和 UX Map 是对上述工具的补充工具包。Icons 提供内联 SVG 渲染(本地文件 + 200,000 多个 Iconify 图标)。Map 提供交互式地图(Leaflet 或 Google Maps),并采用 PHP 优先的配置方式。两者都可在 TwigComponents、LiveComponents 和 Turbo Frames 中使用。Map 还提供专用的 ComponentWithMapTrait,用于在 LiveComponents 中实现响应式地图。

安装

# All core packages
composer require symfony/ux-turbo symfony/stimulus-bundle \
    symfony/ux-twig-component symfony/ux-live-component

# Individual
composer require symfony/stimulus-bundle      # Stimulus
composer require symfony/ux-turbo             # Turbo
composer require symfony/ux-twig-component    # TwigComponent
composer require symfony/ux-live-component    # LiveComponent (includes TwigComponent)
composer require symfony/ux-icons             # UX Icons
composer require symfony/ux-map               # UX Map (then add a renderer below)
composer require symfony/ux-leaflet-map       # Leaflet renderer (free)
composer require symfony/ux-google-map        # Google Maps renderer (requires API key)

常见模式

模式 1:静态组件(TwigComponent)

可复用且不含交互的 UI。适用于按钮、卡片、警告框、徽章。

#[AsTwigComponent]
final class Alert
{
    public string $type = 'info';
    public string $message;
}
{# templates/components/Alert.html.twig #}
<div class="alert alert-{{ type }}" {{ attributes }}>
    {{ message }}
</div>
<twig:Alert type="success" message="Saved!" />
模式 2:组件 + JS 行为(TwigComponent + Stimulus)

带有客户端交互的服务器渲染组件。适用于纯外观交互(切换、动画、第三方 JS 库),且无需服务器数据的场景。

#[AsTwigComponent]
final class Dropdown
{
    public string $label;
}
{# templates/components/Dropdown.html.twig #}
<div data-controller="dropdown" {{ attributes }}>
    <button data-action="click->dropdown#toggle">{{ label }}</button>
    <div data-dropdown-target="menu" hidden>
        {% block content %}{% endblock %}
    </div>
</div>
模式 3:服务器响应式组件(LiveComponent)

根据用户输入通过 AJAX 重新渲染的组件。适用于搜索框、筛选器、带实时验证的表单,以及每次交互都需要服务器数据的场景。

#[AsLiveComponent]
final class SearchBox
{
    use DefaultActionTrait;

    #[LiveProp(writable: true, url: true)]
    public string $query = '';

    public function __construct(
        private readonly ProductRepository $products,
    ) {}

    public function getResults(): array
    {
        return $this->products->search($this->query);
    }
}
<div {{ attributes }}>
    <input data-model="debounce(300)|query" placeholder="Search...">
    <div data-loading="addClass(opacity-50)">
        {% for item in this.results %}
            <div>{{ item.name }}</div>
        {% endfor %}
    </div>
</div>
模式 4:基于框架的导航(Turbo Frame)

局部更新页面,无需完整重新加载。适用于分页、行内编辑、标签式内容、从服务器加载的模态框。

<turbo-frame id="product-list">
    {% for product in products %}
        <a href="{{ path('product_show', {id: product.id}) }}">
            {{ product.name }}
        </a>
    {% endfor %}
</turbo-frame>
模式 5:多区域更新(Turbo Stream)

通过单个服务器响应更新页面中的多个区域。适用于提交表单后需要影响页面多个部分的场景。

#[Route('/comments', methods: ['POST'])]
public function create(Request $request): Response
{
    // ... save comment

    $request->setRequestFormat(TurboBundle::STREAM_FORMAT);
    return $this->render('comment/create.stream.html.twig', [
        'comment' => $comment,
        'count' => $count,
    ]);
}
{# create.stream.html.twig #}
<turbo-stream action="append" target="comments">
    <template>{{ include('comment/_comment.html.twig') }}</template>
</turbo-stream>
<turbo-stream action="update" target="comment-count">
    <template>{{ count }}</template>
</turbo-stream>

你也可以使用 Twig 组件语法:

<twig:Turbo:Stream:Append target="comments">
    {{ include('comment/_comment.html.twig') }}
</twig:Turbo:Stream:Append>
模式 6:Turbo Frame 中的 LiveComponent

组合使用以构建复杂 UI——frame 限定导航范围,LiveComponent 则处理该范围内的响应式交互。

<turbo-frame id="search-section">
    <twig:ProductSearch />
</turbo-frame>
模式 7:实时更新(Mercure + Turbo Stream)

通过 SSE 向所有已连接的浏览器广播服务器端事件。

use Symfony\UX\Turbo\Attribute\Broadcast;

#[Broadcast]
class Message
{
    // Entity changes broadcast automatically
}
<turbo-stream-source src="{{ mercure('chat')|escape('html_attr') }}">
</turbo-stream-source>
<div id="messages">...</div>

何时使用哪种工具

Stimulus —— 为现有 HTML 添加 JS 行为、集成第三方库(图表、日期选择器、地图)、仅在客户端运行的交互(切换、标签页、剪贴板),以及任何需要完全控制 JavaScript 执行的场景。

Turbo Drive —— 类似 SPA 的导航。自动运行,无需配置。只需安装,所有链接和表单都会变为 AJAX。可通过 data-turbo="false" 有选择地禁用。

Turbo Frames —— 加载或更新页面中的单个区域:内联编辑、区域内分页、模态框内容加载、延迟加载侧边栏。

Turbo Streams —— 一次更新多个页面区域、实时广播(使用 Mercure)、表单提交后的闪现消息,以及更新列表和计数器的删除确认。

TwigComponent —— 可复用的 UI 元素(按钮、卡片、提示框、表单控件)、统一的样式和标记、初次渲染后无需与服务器交互,以及组件组合和嵌套。

LiveComponent —— 实时验证表单、实时显示搜索结果、数据绑定(类似 Vue/React,但由服务器渲染),以及任何状态会根据用户交互而变化的组件;适用于希望完全避免编写 JavaScript 的场景。

UX Icons —— 在模板中渲染 SVG 图标。支持 200 多种 Iconify 图标集(Lucide、Tabler、Heroicons、MDI……)和本地 SVG 文件。图标会以内联 <svg> 形式呈现——无需图标字体,也不会发出运行时 HTTP 请求。使用 <twig:ux:icon name="lucide:check" />。

UX Map —— 显示带有标记、多边形、折线、圆形和信息窗口的交互式地图。在 PHP 中构建地图(new Map()),在 Twig 中渲染(ux_map(map))。支持 Leaflet(免费)和 Google Maps。通过 ComponentWithMapTrait,也可在 LiveComponents 中使用响应式地图。

组合使用工具

+-----------------------------------------------------+
|                     Page                            |
|  +------------------------------------------------+ |
|  | Turbo Drive (automatic full-page AJAX)         | |
|  |  +------------------------------------------+  | |
|  |  | Turbo Frame (partial section)            |  | |
|  |  |  +------------------------------------+  |  | |
|  |  |  | LiveComponent (reactive)           |  |  | |
|  |  |  |  +------------------------------+  |  |  | |
|  |  |  |  | TwigComponent (static)       |  |  |  | |
|  |  |  |  |  + Stimulus (JS behavior)    |  |  |  | |
|  |  |  |  +------------------------------+  |  |  | |
|  |  |  +------------------------------------+  |  | |
|  |  +------------------------------------------+  | |
|  +------------------------------------------------+ |
+-----------------------------------------------------+

文件结构

src/
  Twig/
    Components/
      Alert.php              # TwigComponent
      Button.php             # TwigComponent
      SearchBox.php          # LiveComponent
      ProductForm.php        # LiveComponent

templates/
  components/
    Alert.html.twig
    Button.html.twig
    SearchBox.html.twig
    ProductForm.html.twig

assets/
  controllers/
    dropdown_controller.js   # Stimulus
    modal_controller.js      # Stimulus
    chart_controller.js      # Stimulus
  icons/
    close.svg                # UX Icons (local)
    header/
      logo.svg               # UX Icons (namespaced: header:logo)

应避免的反模式

不要将 LiveComponent 用于静态内容。 如果组件在初次加载后从不重新渲染,请改用 TwigComponent——LiveComponent 会带来不必要的开销(AJAX 请求、状态序列化)。

如果用 Frame 就够了,不要使用 Turbo Streams。 如果只更新页面的一个区域,Turbo Frame 更简单,而且不需要特殊的响应格式。

Turbo 能处理的交互,就不要使用 Stimulus。 为链接或表单交互编写 Stimulus 控制器之前,先确认 Turbo Drive/Frames 是否已经能处理。

不要与 Turbo Drive 对着干。 如果链接或表单在 Turbo 下表现异常,通常应确保服务器返回正确的完整 HTML 页面,而不是禁用 Turbo。

图标和地图的反模式

UX Icons 可用时,不要使用图标字体。 内联 SVG 的可访问性更好、更易于设置样式,也不需要额外的 HTTP 请求。

有标记时,不要硬编码地图中心点和缩放级别。 使用 fitBoundsToMarkers() 自动调整视口。

不要忘记为地图容器设置明确的高度。 否则 <div> 会收缩到 0px,地图也就不可见。

不要在启用按需加载图标的情况下部署。 部署前运行 php bin/console ux:icons:lock,以避免运行时向 Iconify API 发出 HTTP 请求。

相关技能

如需了解各工具的详细文档,请阅读对应的技能:

  • Stimulus:控制器、目标、值、操作、出口、延迟加载
  • Turbo:Drive、Frames、Streams、Mercure 集成、<twig:Turbo:Stream:*> 组件
  • TwigComponent:属性、块、计算属性、匿名组件、属性
  • LiveComponent:LiveProp、LiveAction、data-model、表单、emit/listen、轮询、defer/lazy
  • UX Icons:Iconify 按需加载、本地 SVG、图标集、别名、ux:icons:lock CLI
  • UX Map:Leaflet/Google Maps、标记、多边形、折线、圆形、ComponentWithMapTrait

评论

支持 Markdown