跳转到内容

语法参考

本页说明 RSX 语法。Class 工具类范围见 Class 处理,生成 ID 的规则见 ID 与 Key

RSX 元素使用 HTML-like 结构:

rsx! {
<tag attr value={expr}>
{child_expr}
</tag>
}

常见 HTML-like 标签会映射为 div(),因为 GPUI 并没有浏览器 DOM 节点:

分组标签
结构div, span, section, article, header, footer, main, nav, aside
文本h1, h2, h3, h4, h5, h6, p, label, a
表单button, input, textarea, select, form
列表ul, ol, li

无子节点时,自闭合和成对标签等价:

rsx! { <div flex /> }
rsx! { <div flex></div> }

部分标签会直接使用 GPUI 构造器:

RSX生成的构造器
<svg src={path} />svg().path(path)
<img src={source} />img(source)
<img source={source} />img(source)
<canvas prepaint={prepaint} paint={paint} />canvas(prepaint, paint)

img 必须提供 srcsourcecanvas 必须同时提供 prepaintpaint

未知的单段标签会被视为构造函数:

rsx! {
<MyComponent title={"Inbox"} />
}

路径标签会调用对应路径:

rsx! {
<ui::TaskCard title={task.title.clone()} />
}

当 builder 需要参数,或来自组件库时,使用 base={expr}

rsx! {
<Button
base={Button::new("save")}
label={"Save"}
small
/>
}

base 会被宏消费,不会生成 .base(...) 方法调用。

Flag 属性会生成无参方法:

rsx! { <div flex flex_col items_center /> }

Value 属性会生成单参方法:

rsx! {
<div gap={px(16.0)} bg={rgb(0x3b82f6)} />
}

多参数 GPUI 方法使用 tuple 语法:

rsx! {
<div onMouseDown={(MouseButton::Left, handler)} />
}

常见 CamelCase alias 会映射到 GPUI 的 snake_case 方法:

RSX 属性GPUI 方法
onClickon_click
onKeyDownon_key_down
onMouseDownon_mouse_down
textColortext_color
backgroundColorbg
fontSizetext_size
minWidth / maxHeightmin_w / max_h
overflowScrolloverflow_scroll
trackScrolltrack_scroll

whiteSpace 会被显式拒绝。可使用 whitespace-nowrap 等 class,或在 GPUI 可用时直接调用对应方法。

字符串字面量可以直接作为子节点:

rsx! { <div>"Hello"</div> }

Rust 表达式需要放在花括号中:

rsx! {
<div>
{format!("Count: {}", self.count)}
{self.render_footer()}
</div>
}

嵌套元素会生成 .child(...)

rsx! {
<section>
<h1 styled>{"Title"}</h1>
<p>{"Body"}</p>
</section>
}

已有 iterable 可以用 spread 子节点:

rsx! {
<div>
{...rows}
</div>
}

使用 {for pattern in iter { ... }} 渲染迭代器:

rsx! {
<ul>
{for (index, item) in items.iter().enumerate() {
<li>{format!("{}: {}", index, item.name)}</li>
}}
</ul>
}

如果循环体里包含有状态元素,需要添加 key={...}id={...}。详见 ID 与 Key

Fragment 可以返回多个根元素:

rsx! {
<>
<div>{"First"}</div>
<div>{"Second"}</div>
</>
}

Fragment 会展开为 vec![...]。如果根元素具体类型不同,通常需要 into_any_element() 或包一层共同父元素。

子节点中可以使用 Rust 表达式:

rsx! {
<div>
{if let Some(user) = &self.user {
rsx! { <span>{user.name.as_str()}</span> }
} else {
rsx! { <span>{"Guest"}</span> }
}}
</div>
}

使用 when 条件转换当前正在构建的元素:

rsx! {
<div
class="px-4 py-2"
when={(active, |el| el.bg(rgb(0x3b82f6)).text_color(rgb(0xffffff)))}
>
{"Status"}
</div>
}

当条件值是 Option 时使用 whenSome

rsx! {
<div whenSome={(custom_width, |el, width| el.w(px(width)))} />
}

使用 whenClass 应用条件静态 class:

rsx! {
<div
class="px-2"
whenClass={(active, "bg-neutral-900 text-white")}
whenClass={(!active, "text-neutral-600")}
/>
}

whenClass 的 class 必须是字符串字面量,并且会拒绝 overflow-scroll 这类有状态 class。涉及 ID 语义时请使用 when

visible={bool} 会映射到 .visible().invisible(),且表达式只求值一次:

rsx! {
<div visible={self.show_sidebar} />
}

grayscale 会映射为 .grayscale(true)

rsx! {
<img src={source} grayscale />
}

styled 会在用户属性前注入标签默认 class:

标签默认 class
h1text-3xl font-bold
h2text-2xl font-bold
h3text-xl font-bold
h4text-lg font-bold
h5text-base font-bold
h6text-sm font-bold
button, acursor-pointer
input, textareapx-2 py-1
ul, olflex flex-col
liflex items-center
ptext-base
labeltext-sm
formflex flex-col gap-4

用户属性会在 styled 之后应用,因此后续值可以覆盖默认值:

rsx! {
<h1 styled class="text-xl">{title.as_str()}</h1>
}