关于 symfony-ux
Symfony UX 是面向 Symfony 开发者的前端工具集,以服务端渲染 HTML 为基础,按需添加交互。它组合 Stimulus、Turbo、TwigComponent 和 LiveComponent 等工具,涵盖客户端行为、局部更新与响应式组件。
主要功能
- 01Stimulus 添加轻量 JavaScript 行为
- 02Turbo 支持导航和页面区域更新
- 03TwigComponent 构建可复用界面组件
- 04LiveComponent 提供服务端响应式交互
- 05UX Icons 渲染内联 SVG 图标
- 06UX Map 配置交互式地图
适用场景
- 01为现有 HTML 添加切换或标签页交互
- 02无需整页重载更新分页或模态框
- 03构建带实时验证的表单与搜索
- 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 实现无需服务器往返的客户端行为。
快速对比
| 特性 | Stimulus | Turbo | TwigComponent | LiveComponent |
|---|---|---|---|---|
| 是否需要 JavaScript | 是(最少量) | 否 | 否 | 否 |
| 服务器重新渲染 | 否 | 是(页面/框架) | 否 | 是(AJAX) |
| 状态管理 | 仅 JS | URL/服务器 | 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:lockCLI - UX Map:Leaflet/Google Maps、标记、多边形、折线、圆形、
ComponentWithMapTrait