域名 AXUM.RS 将于 2025 年 10 月到期。我们无意再对其进行续费,如果你有意接续这个域名,请与我们取得联系。
  • AXUM.RS 现仅需人民币 3000 元(大写:叁仟元整。接受适度议价
  • 按照行业规则,AXUM.RS 到期后,大概率会进入长时间的赎回期,该期间内,如果你想拥有该域名,将要付出高额的费用
  • 我们已启用 AXUM.EU.ORG 域名,并将持续运营
  • 仅接受微信或支付宝交易
如果你对 AXUM.RS 有兴趣,请和我们进行联系:

dioxus 组件与 rsx

本章将开始第一个案例:计数器的编写。首先,我们需要学习 dioxus 组件的编写和 rsx 语法。

Dioxus 和 React

如果你用过 React,你会发现 Dioxus 和它非常相似,你甚至可以将其视为 rust 版的 React。

  • 和 React 一样,Dioxus 也是通过组件来组织项目的。
  • React 使用 jsx 来编写组件,dioxus 使用 rsx 来编写组件。

如果你愿意,建议花点时间学习一下 React

创建项目

开始之前,我们需要创建一个名为 calc 的 dioxus 项目:

dx new calc

我们来看一下文件结构:

├── assets
│   ├── favicon.ico
│   ├── header.svg
│   └── main.css
├── Cargo.toml
├── Dioxus.toml
├── README.md
└── src
    └── main.rs
  • assets:放置图片、CSS等静态资源
  • Cargo.toml:如果连这都不知道,就不要搞了
  • Dioxus.toml:Dioxus 配置文件
  • src:源代码

初印象

打开 src/main.rs ,我们先来看看 dx 生成的 dioxus Web 项目的代码:

use dioxus::prelude::*;

const FAVICON: Asset = asset!("/assets/favicon.ico");
const MAIN_CSS: Asset = asset!("/assets/main.css");
const HEADER_SVG: Asset = asset!("/assets/header.svg");
fn main() {
    dioxus::launch(App);
}
  • 通过 dioxus::launch() 函数来启动 dioxus 应用
  • 其中的参数 App 是一个组件定义

我们看看 App 组件:

#[component]
fn App() -> Element {
    rsx! {
        document::Link { rel: "icon", href: FAVICON }
        document::Link { rel: "stylesheet", href: MAIN_CSS }
        Hero {}

    }
}

如果没有函数体中的 rsx! 宏,那么,这个函数非常简单,不是吗?

  • #[component] 指令宏,你可以把它看成将某个函数标记为组件──实际上,远非如此,后文会进行讨论。
  • fn App() -> Element:定义一个 App 函数,返回值是 Element
    • 这个函数明显不符合 Rust 的命名规范
      • 在 rust 中,函数的命名规范是蛇形命名
      • 但在 dioxus 中,组件名是大驼峰命名。#[component] 帮我们做了设置,让 rust 编译器不会对组件的命名发出警告
  • rsx! 宏:可以书写 rsx 语法
    • document::Link { rel: "icon", href: FAVICON }:用于添加浏览器的收藏图标
    • document::Link { rel: "stylesheet", href: MAIN_CSS }:用于引入 CSS 样式
    • Hero{}:调用了 Hero 组件
#[component]
pub fn Hero() -> Element {
    rsx! {
        div {
            id: "hero",
            img { src: HEADER_SVG, id: "header" }
            div { id: "links",
                a { href: "https://dioxuslabs.com/learn/0.6/", "📚 Learn Dioxus" }
                a { href: "https://dioxuslabs.com/awesome", "🚀 Awesome Dioxus" }
                a { href: "https://github.com/dioxus-community/", "📡 Community Libraries" }
                a { href: "https://github.com/DioxusLabs/sdk", "  Dioxus Development Kit" }
                a { href: "https://marketplace.visualstudio.com/items?itemName=DioxusLabs.dioxus", "💫 VSCode Extension" }
                a { href: "https://discord.gg/XgGxMSkvUM", "👋 Community Discord" }
            }
        }
    }
}

其中的 rsx 看似很复杂对吗?其实,它对应的 HTML 如下:

<div id="hero">
    <img src="{HEADER_SVG}" id="header" />
    <div id="links">
        <a href="https://dioxuslabs.com/learn/0.6/">📚 Learn Dioxus</a>
        <a href="https://dioxuslabs.com/awesome">🚀 Awesome Dioxus</a>
        ...
		<a href="https://discord.gg/XgGxMSkvUM">👋 Community Discord</a>
    </div>
</div>

清理初始代码

我们先来清理一下初始文件和代码。

  • 首先,删除 assets/headers.svg 文件
  • 然后,打开 src/main.rs
    • 删除 HEADER_SVG常量,即删除 const HEADER_SVG: Asset = asset!("/assets/header.svg");
    • 删除 Hero 组件的定义,以及 App 组件中对该组件的调用

最终,src/main.rs 代码如下:

use dioxus::prelude::*;

const FAVICON: Asset = asset!("/assets/favicon.ico");
const MAIN_CSS: Asset = asset!("/assets/main.css");

fn main() {
    dioxus::launch(App);
}

#[component]
fn App() -> Element {
    rsx! {
        document::Link { rel: "icon", href: FAVICON }
        document::Link { rel: "stylesheet", href: MAIN_CSS }

        div { "Hello, 世界" }
    }
}

定义组件

我们大概看了 dioxus 如何定义组件。现在,我们手动定义一个简单的组件。

fn Hello() -> Element {
    rsx! {
        div { "Hello, 世界" }
    }
}

App 组件中调用:

#[component]
fn App() -> Element {
    rsx! {
        document::Link { rel: "icon", href: FAVICON }
        document::Link { rel: "stylesheet", href: MAIN_CSS }

        Hello {}
    }
}
fn Hello() -> Element {
    let name = "AXUM中文网";
    rsx! {
        div { "Hello, {name}" }
    }
}

rsx 允许使用 {}进行插值操作,如上例的 div { "Hello, {name}" }

现在,我们为Hello 组件添加 Props

#[derive(Props, Clone, PartialEq)]
struct HelloProps {
    name: String,
}
fn Hello(props: HelloProps) -> Element {
    rsx! {
        div { "Hello, {props.name}" }
    }
}
  • HelloProps 结构体就是 Hello 组件的 Props,它包含一个 name 成员
    • 它需要 #[derive(Props, Clone, PartialEq)]
  • fn Hello(props: HelloProps)Hello 组件接收 HelloProps 类型的参数

对于组件而言,Props是一个非常重要且常用的。如果每个组件的Props都要这样单独定义一个结构体,那会非常麻烦。好在 dioxus 提供了 #[component],以下代码和上述代码效果一样:

#[component]
fn Hello(name: String) -> Element {
    rsx! {
        div { "Hello, {name}" }
    }
}

#[component] 宏:

  • 自动将组件的 Props (参数)定义对应的结构体
  • 会消除函数命名不规范的警告

rsx

dioxus 使用的是一种称为 rsx 的语法来生成虚拟DOM。我们来看一下最简单的 rsx:

它将生成对应的虚拟DOM:

<div>
    Hello, 世界
</div>

由此可知:在 rsx 中,某个元素中的字符串,就是它渲染到页面的文本内容。

rsx! {
    div { 
        id:"msg",
        class:"text-red-500",
        "Hello, 世界"
     }
}

我们给 div 添加了 idclass 属性,对应的 HTML:

<div id="msg" class="text-red-500">
    Hello, 世界
</div>

由此可知:rsx 的属性是键值对,多个属性需要用逗号分割。

要查看完整内容,请先登录