Filament Loading Indicator 组件完全指南:内置用法与自定义实现
【免费下载链接】filamentA powerful open-source UI framework for Laravel • Build and ship apps & admin panels fast with Livewire项目地址: https://gitcode.com/GitHub_Trending/fi/filament
Filament 的 Loading Indicator(加载指示器)是一个基于 SVG 的轻量级动画组件,用于在页面、表单、按钮或表格等待异步操作完成时给出视觉反馈。本文将以 Loading Indicator 官方文档 为核心,结合仓库源码(support 包)与测试用例,完整讲解该组件的标准用法、样式规则、无障碍处理,以及如何通过服务容器替换为自定义加载动画的实现方案。
读完本文,你将能够在 Filament 应用中直接使用<x-filament::loading-indicator>组件,理解其底层渲染链路(Blade 组件 →generate_loading_indicator_html()→ 契约绑定),并掌握在服务提供者中绑定自定义LoadingIndicator实现以全局替换默认 spinner 的完整步骤。
组件简介与基本用法
Loading Indicator 是一个动画 SVG,用于向用户提示"某操作正在进行中"。它不依赖任何 JavaScript 初始化,渲染后即具备旋转动画,可以直接嵌入到任意 Blade 视图中:
<x-filament::loading-indicator class="h-5 w-5" />该组件会输出一个双圆环风格的旋转 SVG:外层为opacity="0.2"的完整圆环,内层为填充色弧段,通过 CSS 旋转动画呈现"转圈"效果。class属性用于控制尺寸——在上述示例中h-5 w-5将其限制为 20×20 像素。默认动画定义在 loading-indicator.css:
.fi-loading-indicator { @apply motion-safe:animate-spin; }注意motion-safe:前缀:动画仅在系统未开启"减少动态效果"(prefers-reduced-motion)时生效,这符合可访问性最佳实践。
组件内部的实现方式
<x-filament::loading-indicator>的 Blade 视图本身只有一行,核心逻辑委托给 support 包的全局函数:
{{ \Filament\Support\generate_loading_indicator_html($attributes) }}对应源码见 loading-indicator.blade.php。而 helpers.php 中的generate_loading_indicator_html()函数负责:
- 默认使用
IconSize::Medium尺寸(若未显式指定); - 将传入属性合并进
fi-icon fi-loading-indicator及fi-size-*样式类; - 解析并缓存
LoadingIndicator契约实例; - 调用实例的
toHtml()方法生成 HTML。
if (! function_exists('Filament\Support\generate_loading_indicator_html')) { function generate_loading_indicator_html(?ComponentAttributeBag $attributes = null, ?IconSize $size = null): Htmlable { $size ??= IconSize::Medium; $attributes = ($attributes ?? new FilamentComponentAttributeBag)->class([ 'fi-icon fi-loading-indicator', "fi-size-{$size->value}", ]); static $loadingIndicator = null; $loadingIndicator ??= app(LoadingIndicator::class); return new HtmlString($loadingIndicator->toHtml($attributes)); } }从源码结构可以看出,加载指示器在整个 Filament 生态中是统一复用的:按钮、链接、徽章、下拉菜单项、输入框、文件上传等组件在进入加载状态时,都会通过同一套机制渲染同一个 spinner(例如 icon-button.blade.php 会在wire:loading状态下调用该函数)。因此,替换加载指示器实现即可一次性影响所有组件。
替换默认的加载指示器
Filament 通过 Laravel 的服务容器解耦了加载指示器的渲染逻辑。默认情况下,Filament\Support\Contracts\LoadingIndicator契约绑定到Filament\Support\View\DefaultLoadingIndicator实现,绑定声明位于 SupportServiceProvider.php:
$this->app->bind(LoadingIndicator::class, DefaultLoadingIndicator::class);要换成你自己的实现,只需在某个服务提供者中重新绑定该契约:
use App\Support\CustomLoadingIndicator; use Filament\Support\Contracts\LoadingIndicator; public function register(): void { $this->app->bind(LoadingIndicator::class, CustomLoadingIndicator::class); }仓库测试 LoadingIndicatorTest.php 精确验证了这条替换链路:
it('allows swapping the loading indicator implementation via the container', function (): void { app()->bind(LoadingIndicator::class, CustomLoadingIndicator::class); expect(app(LoadingIndicator::class)) ->toBeInstanceOf(CustomLoadingIndicator::class); expect(app(LoadingIndicator::class)->toHtml(new ComponentAttributeBag)) ->toBe('<div>Custom</div>'); });测试还确认了两个默认行为:LoadingIndicator契约默认解析为DefaultLoadingIndicator实例;DefaultLoadingIndicator::toHtml()输出<svg>并原样透传传入的属性(测试断言fi-custom出现在输出中)。
契约与实现的要求
你的自定义类必须实现LoadingIndicator契约。该契约定义在 Contracts/LoadingIndicator.php,只有一个方法:
interface LoadingIndicator { public function toHtml(ComponentAttributeBag $attributes): string; }toHtml()接收一个Illuminate\View\ComponentAttributeBag(已包含类名、尺寸、wire:target、wire:loading等运行时属性),并返回加载指示器的 HTML 字符串:
namespace App\Support; use Filament\Support\Contracts\LoadingIndicator; use Illuminate\View\ComponentAttributeBag; class CustomLoadingIndicator implements LoadingIndicator { public function toHtml(ComponentAttributeBag $attributes): string { return <<<HTML <svg {$attributes->toHtml()}> <!-- ... --> </svg> HTML; } }传入的$attributes中已经包含fi-icon fi-loading-indicator以及尺寸相关的 hook 类(如fi-size-md),因此请将它们直接转发到你的根元素上,以保证样式系统、动画钩子与组件缓存机制正常工作。
参考:默认实现的细节
了解默认实现 DefaultLoadingIndicator.php 有助于你写出等价的替换实现。它做了两件事:
- 合并无障碍属性:默认将
aria-hidden="true"合并进属性并标记escape: false(即不转义该值),避免屏幕阅读器重复播报"加载中"。注释说明:spinner 是装饰性元素,加载状态本身由外层控件的aria-label或role="status"传达;调用方仍可通过显式传入自己的aria-hidden覆盖此默认值。 - 输出标准双圆环 SVG:外层圆环
opacity="0.2"、内层实心弧段均使用currentColor填充,因此颜色自动跟随父元素的文本颜色。
public function toHtml(ComponentAttributeBag $attributes): string { $attributes = $attributes->merge(['aria-hidden' => 'true'], escape: false); return <<<HTML <svg fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" {$attributes->toHtml()} > <path clip-rule="evenodd" d="..." fill-rule="evenodd" fill="currentColor" opacity="0.2"></path> <path d="..." fill="currentColor"></path> </svg> HTML; }自定义实现时建议保留aria-hidden="true"的默认合并逻辑(或按你的场景自行决定),并注意toHtml()返回的是字符串而非视图,若想在返回内容中包含 Blade 渲染结果,请先自行编译成字符串。
性能与缓存注意事项(重要)
仓库源码明确展示了实例的缓存策略:generate_loading_indicator_html()内部使用static $loadingIndicator静态变量,首次解析后便在整个 PHP 进程生命周期内缓存LoadingIndicator实例:
static $loadingIndicator = null; $loadingIndicator ??= app(LoadingIndicator::class);这意味着:
- 在普通 FPM 环境下,每个请求进程各自缓存,绑定在
register()中声明即可; - 在Laravel Octane等常驻内存环境下,该实例在 worker 启动时只解析一次,不会在请求之间重新解析。因此你的绑定必须在服务提供者中完成,切勿在运行时(如中间件、控制器、Blade 视图中)动态重新绑定,否则不会生效。
这一点在原文档中亦有明确警告:绑定请始终放在服务提供者的register()方法里,保持绑定时机在进程启动阶段。
实际使用场景:与 Livewire 加载状态配合
虽然本组件可独立使用,但更常见的场景是结合 Livewire 的wire:loading指令实现"异步请求期间显示 spinner":
<button wire:click="save"> <span wire:loading.remove wire:target="save">保存</span> <x-filament::loading-indicator wire:loading.delay.default wire:target="save" class="h-4 w-4" /> </button>Filament 内部组件也是同样的模式。以 icon-button.blade.php 为例:当存在wire:click/wire:target目标或表单提交时,组件会判断hasLoadingIndicator,然后通过wire:loading.attr禁用按钮,并用wire:loading.delay.<delay>控制 spinner 的显示时机:
'wire:loading.remove.delay.' . $loadingDelay => $hasLoadingIndicator, 'wire:target' => $hasLoadingIndicator ? $loadingIndicatorTarget : false,其中$loadingDelay读取自配置文件filament.php的livewire_loading_delay项(默认'default',即 Livewire 标准的 200ms 延迟)。该配置定义在 packages/support/config/filament.php,并支持两种特殊取值:
'none':指示器立即显示,适合高延迟网络环境;'default':应用 Livewire 的 200ms 标准延迟。
这一延迟机制避免了对极短请求的"闪烁"体验,是 Filament 全站加载反馈的一致基础。
进阶:表单字段内的加载指示器
在表单系统中,加载指示器还承担"字段联动请求"的反馈职责。以 FileUpload.php 为例,文件上传字段支持通过loadingIndicatorPosition()配置 spinner 出现的位置(默认'right',也可设为'left'等):
protected string | Closure $loadingIndicatorPosition = 'right'; public function loadingIndicatorPosition(string | Closure | null $position): static { $this->loadingIndicatorPosition = $position; return $this; } public function getLoadingIndicatorPosition(): string { return $this->evaluate($this->loadingIndicatorPosition); }对应的测试见 FileUploadTest.php,它验证了位置取值'right'与'left'的读写行为。同理,Field.php 中字段输入框也会在存在wire:target目标时渲染加载指示器。这说明 Loading Indicator 组件贯穿 Filament 的按钮、链接、表单字段、表格头部等全部交互点,是统一的"进程反馈"基建。
小结
- 使用:
<x-filament::loading-indicator class="h-5 w-5" />即可渲染一个随系统减少动态效果偏好而启停的旋转 SVG; - 样式:尺寸通过
class控制,颜色跟随currentColor,默认带motion-safe:animate-spin动画; - 替换:在服务提供者
register()中bind(LoadingIndicator::class, YourImplementation::class),实现toHtml(ComponentAttributeBag): string,并将传入属性转发给根元素; - 契约与缓存:契约只含一个
toHtml()方法;实例在进程生命周期内被静态缓存,Octane 下务必在启动阶段绑定; - 无障碍:默认实现合并
aria-hidden="true",加载状态语义由外层控件承担; - 验证依据:默认绑定、SVG 透传、容器替换三条行为均有 LoadingIndicatorTest.php 测试覆盖。
无论是直接使用默认 spinner,还是通过容器注入自定义品牌加载动画,Filament 的 Loading Indicator 组件都提供了足够简单且可扩展的接入方式。
【免费下载链接】filamentA powerful open-source UI framework for Laravel • Build and ship apps & admin panels fast with Livewire项目地址: https://gitcode.com/GitHub_Trending/fi/filament
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考