第二章:RSX 语法与组件化开发
博客v1.0系列教程(Dioxus)博客 v1.0 系列教程 (Dioxus)
第二章:RSX 语法与组件化开发
1. RSX 语法详解
RSX 是 Dioxus 的声明式 UI DSL(领域特定语言),在 rsx! 宏内部使用。
1.1 元素与属性
rsx! {
// 自闭合元素
br {}
hr {}
input { r#type: "text" }
// 带子元素的容器
div { class: "wrapper",
p { "文本内容" }
}
// 多个属性
img {
src: "/logo.png",
alt: "logo",
class: "w-16 h-16 rounded-full",
}
}
Rust 关键字作为属性名
某些 HTML 属性恰好是 Rust 关键字,需要使用 r# 前缀:
rsx! {
input { r#type: "text" }
label { r#for: "email" }
input { r#type: "checkbox", checked: "false" }
// loop 也是关键字,但在 RSX 中不需要 r#
}
1.2 文本插值
let title = "Dioxus 教程";
let count = 42;
let items = vec!["A", "B", "C"];
rsx! {
// 直接插入变量
h1 { "{title}" }
// 表达式
p { "总数: {count + 10}" }
// 方法调用
p { "大写: {title.to_uppercase()}" }
// 条件表达式
p { "{if count > 0 { \"有数据\" } else { \"空\" }}" }
// 格式化
p { "价格: {format!(\"${:.2}\", 19.99)}" }
}
1.3 循环
let articles = vec!["文章A", "文章B", "文章C"];
rsx! {
// 方式一:for 循环(推荐)
ul {
for item in &articles {
li { key: "{item}", "{item}" }
}
}
// 方式二:迭代器
ul {
{articles.iter().map(|item| rsx! {
li { "{item}" }
})}
}
}
2. 组件定义
2.1 函数组件
#[component]
fn Greeting(name: String) -> Element {
rsx! {
div { class: "greeting",
h2 { "你好, {name}!" }
}
}
}
规则:
- 函数名首字母大写
- 标注
#[component]宏 - 参数就是 Props
- 返回
Element - 必须实现
PartialEq和Clone
2.2 Props 进阶
#[component]
fn Card(
title: String,
subtitle: Option<String>, // 可选属性
children: Element, // 子元素插槽
) -> Element {
rsx! {
div { class: "card",
h3 { "{title}" }
if let Some(ref sub) = subtitle {
p { class: "text-sm text-gray-500", "{sub}" }
}
div { class: "card-body", {children} }
}
}
}
// 使用
Card {
title: "Dioxus 教程".to_string(),
subtitle: Some("深入浅出".to_string()),
p { "这是卡片正文内容" }
}
2.3 组件组合
#[component]
function BlogCard(summary: ArticleSummary) -> Element {
rsx! {
Card { title: summary.title,
p { "{summary.abstract_field}" }
span { class: "tag", "{summary.category}" }
}
}
}
#[component]
function ArticleList() -> Element {
let articles = vec![
ArticleSummary { title: "第一篇", .. },
ArticleSummary { title: "第二篇", .. },
];
rsx! {
div { class: "space-y-4",
for article in &articles {
BlogCard { summary: article.clone() }
}
}
}
}
3. 条件渲染
#[component]
function UserGreeting(user: Option<User>) -> Element {
rsx! {
div { class: "user-area",
// if-else
if let Some(u) = user {
span { "欢迎回来, {u.name}" }
button { "退出" }
} else {
a { href: "/login", "登录" }
a { href: "/register", "注册" }
}
}
}
}
// 更灵活的条件
rsx! {
div {
// 仅当条件满足时渲染
if is_loading() {
div { class: "spinner" }
}
// match 多分支
match status() {
Status::Loading => rsx! { div { "加载中..." } },
Status::Success(data) => rsx! { div { "{data}" } },
Status::Error(e) => rsx! { div { class: "error", "{e}" } },
}
}
}
4. Fragment(片段)
当需要返回多个同级元素而不添加额外 DOM 节点时:
rsx! {
// 隐式 Fragment:rsx! 宏自动包裹
h1 { "标题" }
p { "段落1" }
p { "段落2" }
}
// 显式 Fragment
rsx! {
Fragment {
h1 { "标题" }
p { "段落1" }
}
}
5. Props 传递模式
// 方式一:直接传递
Button { label: "提交" }
// 方式二:展开传递
let btn_props = ButtonProps { label: "提交".into() };
Button { ..btn_props.clone() }
// 方式三:children 插槽
Card { title: "温馨提示".to_string(),
p { "这是一条重要通知" }
button { "知道了" }
}
6. 小结
- RSX 是声明式 UI 语法,类似 JSX 但内嵌在 Rust 中
#[component]宏将函数转换为组件- Props 支持必填、可选(
Option)和 children 插槽 rsx!内可以直接写if、match、for等 Rust 表达式- Fragment 用于返回多个同级元素
- 下一章将学习状态管理与 Signal 响应式系统
dioxusrsxcomponentselements