跳转到内容

Class 处理

class 属性会把 Tailwind-like 工具类子集映射为 GPUI builder 方法。

rsx! {
<div class="flex flex-col gap-4 p-4 bg-blue-500 text-white">
{"Content"}
</div>
}

class 列表在编译期已知时,优先使用字符串字面量。它们会由宏解析,并直接展开为 GPUI 方法调用:

rsx! {
<div class="flex items-center gap-2 px-4 py-2 rounded-md" />
}

permissive 模式下,无法安全生成的未知静态 class 会被忽略;无效的任意值仍会编译时报错。strict 模式下,不支持的静态 class 会直接变成编译错误:

use gpui_rsx::rsx_strict;
rsx_strict! {
<div class="hover:bg-blue-500" />
}

如果 ifmatch 的分支都是字符串字面量,宏也可以在编译期展开:

rsx! {
<div class={if active { "bg-blue-500 text-white" } else { "bg-gray-100 text-gray-900" }} />
}
rsx! {
<div class={match state {
State::Ready => "border-green-500",
State::Error => "border-red-500",
_ => "border-gray-300",
}} />
}

这种写法可以保留状态样式的可读性,同时避免简单字面量分支走动态 class matcher。优化只作用于 class={...} 内部的表达式;如果先赋值给变量,再把变量传给 class,就会走普通动态 class 路径。

状态样式可以用静态 class 表达时,使用 hoverClassfocusClassactiveClass

rsx! {
<button
class="px-4 py-2 rounded-md bg-blue-500 text-white"
hoverClass="bg-blue-600"
focusClass="border-blue-500"
activeClass="opacity-75"
/>
}

这些属性会展开为 GPUI StyleRefinement 闭包。它们只接受字符串字面量;overflow-scrolldebug-outline 这类需要元素级 stateful 行为的 class 会产生编译错误。由于当前 GPUI 的 active hook 是 stateful,activeClass 会自动注入 ID;hoverClassfocusClass 不会。

当 class 字符串必须在运行时组装时,再使用动态 class:

let class_name = format!("w-[{}px] bg-{}", width, tone);
rsx! {
<div class={class_name} />
}

动态 class 使用生成的运行时 matcher。matcher 支持常用布局、间距、尺寸、颜色、透明度、排版和边框工具类,也支持文档列出的任意长度和颜色格式。

permissive 模式下,未知动态 class 会被忽略。debug 构建中,每个生成调用点的未知 class 警告只输出一次。strict 模式下,不支持的动态 class 在求值时会 panic。

能力静态 class动态 class
布局和对齐支持支持子集
数值间距和尺寸支持支持
任意长度支持支持
百分比和分数尺寸支持支持
完整 Tailwind 色板支持支持
任意 hex / RGB / RGBA 颜色支持支持
静态有状态滚动 class支持并自动生成 ID不支持
hover:* 等 Tailwind variant忽略或 strict 报错忽略或 strict panic
范围说明
布局flexflex-colflex-rowflex-wrapgridhiddenabsoluterelative、grow/shrink 辅助类。
对齐GPUI 支持的 items-*justify-*content-*self-*
间距gap-*gap-x-*gap-y-*p-*px-*m-* 以及方向变体。
尺寸w-*h-*size-*min-*max-**-full*-auto、百分比、分数和任意长度。
颜色完整 Tailwind 色名,以及任意 hexrgbrgba 形式。
排版字号、字重、文字对齐、装饰、截断、whitespace 和 line clamp。
边框宽度、方向宽度、颜色、圆角和 dashed border。
其他cursor helper、debug-outline、阴影、透明度、grid placement alias。

GPUI-RSX 的数值工具类使用直接像素语义:

rsx! {
<div class="w-64 gap-4" />
}

它会展开为类似 w(px(64.0)).gap(px(4.0)),不会使用 Tailwind CSS 的 rem 间距比例。

任意长度示例:

rsx! {
<div class="w-[280px] h-[18rem] max-w-[37.5%] gap-[14px]" />
}

百分比和分数支持在尺寸类中使用:

rsx! {
<aside class="w-6/24 max-w-[40%]" />
}

gap-[10%] 这类百分比间距会被拒绝,因为 GPUI 间距 API 需要确定长度。

命名 Tailwind 颜色使用 family-shade

rsx! {
<div class="bg-blue-500 text-white border-gray-300" />
}

支持的色系包括 slategrayzincneutralstoneredorangeamberyellowlimegreenemeraldtealcyanskyblueindigovioletpurplefuchsiapinkrose,色阶为 50950,另有 blackwhite

任意颜色支持 hex、短 hex、RGB 和 RGBA:

rsx! {
<div class="bg-[#ff0000] text-[#f00] border-[rgba(15,23,42,0.35)]" />
}

需要让未知静态 class 直接失败时,使用 rsx_strict!

use gpui_rsx::{rsx_expand, rsx_permissive, rsx_strict};
rsx_strict! { <div class="flex w-[280px]" /> }
rsx_permissive! { <div class="hover:bg-blue-500 flex" /> }
let preview = rsx_expand! {
<div class="flex gap-4 bg-blue-500" />
};

rsx_expand! 会返回生成的 GPUI builder 链字符串,适合调试宏展开;它不会对生成的 GPUI 表达式做类型检查。

  • 稳定 UI 结构使用字面量 class。
  • 少量状态分支使用字面量 ifmatch
  • 只有尺寸或颜色确实运行时变化时再生成动态 class。
  • 热路径中,如果值本来就是 Rust 表达式,优先使用直接 GPUI 属性,例如 w={px(width)}
  • 少量条件变体优先使用 whenwhenClass,不要拼接很长的运行时 class 字符串。

关于 render 循环分配、ID/key 行为和验证命令,见性能优化