显示侧边栏
指南
隐藏侧边栏

模式

Flux 中的每一个设计决策都是经过深思熟虑、带着明确意图做出的。理解这些设计意图,会让你使用 Flux 的体验更加直观自然。


理想情况下,内化了部分 Flux 的设计哲学之后,你几乎可以“猜到”如何使用一个自己尚不熟悉的组件。

Props 与 Attributes

从表面上看,Props 与 Attributes 很相似;不过我们发现,将两者区分开会更有帮助:Flux 提供的配置称为 “props”,而针对底层原生 HTML 元素会被原样转发的自定义配置称为 “attributes”。
例如,下面这个组件同时使用了名为 variant 的 prop,以及一个自定义的 x-on:change.prevent 属性:
<flux:button variant="primary" x-on:change.prevent="...">
当该组件在浏览器中渲染时,输出的 HTML 可能类似这样:
<button type="button" class="bg-zinc-900 ..." x-on:change.prevent="...">
如上所示,x-on:change.prevent 属性会被直接转发到底层的原生 HTML 元素,而 variant 这个 prop 则会在组件内部用于决定应用到输入控件上的类名。

类名合并

大多数 Flux 组件都会为其底层元素应用 Tailwind 类名;同时你也可以在使用组件时传入自定义类名,Flux 会与其内部类名自动进行合并。
例如,传入 w-full 这个 Tailwind 工具类,就可以让按钮占满整行宽度:
<flux:button class="w-full">
这样,当组件渲染出来时,输出的 HTML 会同时包含你传入的 w-full 以及 Flux 内部应用的其他类名:
<button type="button" class="w-full border border-zinc-200 ...">

处理类名合并冲突

有时,当你传入的某个 Tailwind 工具类与 Flux 内部也会应用的类名发生重叠时,就会出现冲突。
比如,你可能会通过传入 bg-zinc-800 这个 Tailwind 工具类来定制按钮的背景色:
<flux:button class="bg-zinc-800 hover:bg-zinc-700">
但由于 Flux 自身也会应用一组 bg-* 相关的类名,最终输出的 HTML 会同时包含这两类样式:
<button type="button" class="bg-zinc-800 hover:bg-zinc-700 bg-white hover:bg-zinc-100...">
由于两个类名同时存在于同一个元素上,CSS 中“后定义者优先”的规则会生效,后出现的样式将覆盖前者。
Tailwind 的 important(!)修饰符是解决这类冲突最简单的方式:
<flux:button class="bg-zinc-800! hover:bg-zinc-700!">
两组存在冲突的工具类仍会同时渲染,但带有 ! 修饰符的那组用户定义样式将获得更高优先级。
不过,滥用 ! 修饰符有时会带来比解决更多的问题。下面是几个可替代的思路:
  • 发布该组件并添加你自己的 variant(变体)
  • 通过样式化组件的 data 属性来进行全局定制
  • 为你的特定场景编写一个新的组件

拆分属性转发

在理想情况下,每个 Flux 组件都只会直接渲染一个单一的 HTML 元素。但在实际中,有些组件会更复杂一些。
例如,flux:input 组件默认情况下会渲染两个 HTML 元素:一个包裹用的 <div>,以及一个底层的 <input> 元素:
<div class="...">    <input type="text" class="..."></div>
这会带来一个问题:当你向组件传入自定义的 Tailwind 类名时——这些类名究竟应该应用到哪个元素上?
在这种情况下,Flux 通常会把需要转发的属性拆分为两类:一类是与样式相关的,比如 class;另一类是与行为相关的,比如 autofocus。随后,组件会把它们分别应用到最合适的元素上。
例如,考虑同时传入如下的 classautofocus 属性:
<flux:input class="w-full" autofocus>
Flux 会把 w-full 这个类名应用到外层包裹的 <div> 元素上,而把 autofocus 这个 attribute 应用到底层的 <input> 元素上:
<div class="w-full ...">    <input type="text" class="..." autofocus></div>

常见 Props

了解了 Attributes 的一些细节后,接下来看看在使用 Flux 时会遇到的常见 Props 模式。

变体

首先是 “variant”。只要组件提供了另一种视觉风格,就会通过 variant 这个 prop 来实现。
下面是一些常见组件的可用变体示例:
<flux:button variant="primary" /><flux:input variant="filled" /><flux:modal variant="flyout" /><flux:badge variant="solid" /><flux:select variant="combobox" /><flux:separator variant="subtle" /><flux:tabs variant="segmented" />
如果你需要的变体目前未内置,欢迎发布该组件并添加你自己的变体

图标

与其在插槽中手动添加、样式化和控制图标的间距,你可以直接向支持该功能的组件传入 icon 这个 prop。
Flux 的图标来自 Heroicons。这是一套由 Tailwind Labs 打造的美观且实用的图标集。
以下是一些常见的可接收图标的组件:
<flux:button icon="magnifying-glass" /><flux:input icon="magnifying-glass" /><flux:tab icon="cog-6-tooth" /><flux:badge icon="user" /><flux:breadcrumbs.item icon="home" /><flux:navlist.item icon="user" /><flux:navbar.item icon="user" /><flux:menu.item icon="plus" />
有些组件除了默认在前面添加图标外,还支持把图标放到元素末尾。这种情况下,你可以同时传入 icon:trailing 这个 prop:
<flux:button icon:trailing="chevron-down" /><flux:input icon:trailing="credit-card" /><flux:badge icon:trailing="x-mark" /><flux:navbar.item icon:trailing="chevron-down" />

尺寸

部分组件通过 size 这个 prop 提供不同的尺寸变体。
以下是一些可以缩小(变小号)的组件:
<flux:button size="sm" /><flux:select size="sm" /><flux:input size="sm" /><flux:tabs size="sm" />
下面是一些适合在更大视觉场景中放大(变大号)的组件:
<flux:heading size="lg" /><flux:badge size="lg" />

键盘提示

icon 类似,许多组件允许通过 kbd 这个 prop 添加用于提示快捷键的装饰性标注:
<flux:button kbd="⌘S" /><flux:tooltip kbd="D" /><flux:input kbd="⌘K" /><flux:menu.item kbd="⌘E" /><flux:command.item kbd="⌘N" />

内嵌

部分组件提供 inset 这个 prop,用于按你指定的轴向,为元素添加恰到好处的负外边距,从而实现“内嵌”效果。
这在需要让按钮或徽章在文字中内联显示、且又不拉伸整个容器高度时非常有用。
<flux:badge inset="top bottom"><flux:button variant="ghost" inset="left">

Prop 转发

在某些情况下,一个组件会包裹另一个组件,并以简单的 prop 形式对外暴露其能力。
例如,Button 组件允许你直接通过简单的 icon prop 设置图标,而无需把完整的 Icon 组件作为子元素传入 Button:
<flux:button icon="bell" />
相比完全手动组合组件,这通常是更简洁、也更可取的方式:
<flux:button>   <flux:icon.bell /></flux:button>
不过,当你通过 icon 这个 prop 使用图标时,无法再像直接嵌入 Icon 组件那样,为其传递额外的 props(例如 variant)。
在这种情况下,Flux 通常会通过带前缀的“嵌套 props”来暴露子组件的能力,例如:
<flux:button icon="bell" icon:variant="solid" />

选择性关闭的 Props

有时,Flux 会默认将某个 prop 设为“开启”状态,或在内部管理它的状态。例如,navbar.item 组件上的 current prop。
但在某些具体实例中,你可能希望强制其值始终保持为 false
在这种情况下,你可以使用 Laravel 的动态 prop 语法(:),并显式传入 false
<flux:navbar.item :current="false">

简写 Props

当某种组件组合既常见又显得冗长时,通常就值得提供一种更简洁的“简写”语法。
例如,下面是一个完整的输入字段:
<flux:field>    <flux:label>Email</flux:label>    <flux:input wire:model="email" type="email" />    <flux:error name="email" /></flux:field>
这可以简写为如下形式:
<flux:input type="email" wire:model="email" label="Email" />
在内部,Flux 会将上述简写自动展开为完整的 fieldlabelinputerror 组件组合。
这样既能保持语法简洁,又能在需要时使用完整的长格式语法进行充分自定义。
你也会在 tooltip 与 button 的组合中遇到这一模式。
下面是一个冗长写法的 tooltip:
<flux:tooltip content="Settings">    <flux:button icon="cog-6-tooth" /></flux:tooltip>
由于上面的写法经常会重复,因此可以将其简化为一个 tooltip 的简写 prop:
<flux:button icon="cog-6-tooth" tooltip="Settings" />

数据绑定

在 Livewire 中,你通常会直接在表单的输入元素上添加 wire:model
在 Flux 中,体验并无二致。以下是一些你经常会添加 wire:model 的常见组件:
<flux:input wire:model="email" /><flux:checkbox wire:model="terms" /><flux:switch wire:model.live="enabled" /><flux:textarea wire:model="content" /><flux:select wire:model="state" />
除了这些常见组件外,下面还有一些也可以通过 wire:model 进行控制:
<flux:checkbox.group wire:model="notifications"><flux:radio.group wire:model="payment"><flux:tabs wire:model="activeTab">
当然,你也可以向这些组件传入 x-modelx-on:change,其行为将与原生输入元素完全一致。

组件分组

当一个 Flux 组件既能独立使用、又可被“分组”使用时,其用于包裹的分组组件通常会带有 .group 后缀。
下面这些组件既可以单独使用,也可以成组使用:
<flux:button.group>    <flux:button /></flux:button.group><flux:input.group>    <flux:input /></flux:input.group><flux:checkbox.group>    <flux:checkbox /></flux:checkbox.group><flux:radio.group>    <flux:radio /></flux:radio.group>
另外,如果某个组件无法单独使用、只能以“分组”形式出现,那么包裹组件通常直接使用该组件的主名称,而每个子项会带有 .item 后缀:
<flux:accordion>    <flux:accordion.item /></flux:accordion><flux:menu>    <flux:menu.item /></flux:menu><flux:breadcrumbs>    <flux:breadcrumbs.item /></flux:breadcrumbs><flux:navbar>    <flux:navbar.item /></flux:navbar><flux:navlist>    <flux:navlist.item /></flux:navlist><flux:navmenu>    <flux:navmenu.item /></flux:navmenu><flux:command>    <flux:command.item /></flux:command><flux:autocomplete>    <flux:autocomplete.item /></flux:autocomplete>

根组件

大多数情况下,当一个组件可以由许多子组件组合而成时,你会看到类似上文提到的“复合命名”形式。
但对于一些体量更大、或更加“基础(primitive)”的组件,它们通常不会使用统一前缀,而是直接使用组件本身的名称。
例如,下面是 Flux 的 field 组件:
<flux:field>    <flux:label></flux:label>    <flux:description></flux:description>    <flux:error></flux:error></flux:field>
你会注意到这里使用的是“裸”组件名,比如 flux:label,而不是 flux:field.label
这样设计的原因,是为了避免常用组件出现过度冗长的层级命名,比如会变成:flux:field.label.badge
<flux:table> 组件中也采用了同样的模式。

特例

虽然不太“优雅”,但为了让体验“感觉正确”,有时不得不在某些地方舍弃一致性。
例如,flux:tabs 组件就没有完全遵循上述规则:
<flux:tab.group>    <flux:tabs>        <flux:tab>    </flux:tabs>    <flux:tab.panel></flux:tab.group>

插槽

总体来说,Flux 更倾向于通过组合多个组件来构建界面,而不是依赖插槽。
不过,在某些情况下,没有比插槽更合适的替代方案,插槽反而是完美的解决方式。
举例来说,先看下面这个带有 x-mark 图标的输入组件:
<flux:input icon:trailing="x-mark" />
如果你希望把该图标包在一个可点击的按钮中以执行操作,那么不借助插槽(或不引入极为冗长的自定义语法)将无法实现。
下面是将其改写为使用包裹按钮之后的示例:
<flux:input>    <x-slot name="iconTrailing">        <flux:button icon="x-mark" size="sm" variant="subtle" wire:click="clear" />    </x-slot></flux:input>

小坑

下面是一些在使用 Flux 时可能遇到的易踩“坑”。

Blade 组件 vs HTML 元素

在 Blade 中处理普通 HTML 元素时,你可以在开始标签内自由使用诸如 @if 的表达式。
然而,在 Blade 或 Flux 组件的开始标签中,不支持这类表达式。
相应地,你需要遵循 Blade 组件的动态属性语法
<!-- Conditional attributes: --><input @if ($disabled) disabled @endif><flux:input :disabled="$disabled">
© 2025 FluxProMax™. All Rights Reserved.

备案号:皖ICP备13019729号-11