第二章: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
  • 必须实现 PartialEqClone

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! 内可以直接写 ifmatchfor 等 Rust 表达式
  • Fragment 用于返回多个同级元素
  • 下一章将学习状态管理与 Signal 响应式系统
dioxusrsxcomponentselements