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

计数器

本章我们将开始编写计数器。你将学习到:事件处理、状态管理、组件通讯、集成 Tailwind CSS 等知识。

UI

首先,我们编写计数器UI:

#[component]
fn CalcView() -> Element {
    rsx! {
        div { "当前计数:123" }
        div {
            button { "-1" }
            button { "+1" }
        }
    }
}

对应 HTML:

<div>
    当前计数:123
</div>
<div>
    <button>
        -1
    </button>
    <button>
        +1
    </button>
</div>

为了响应按钮(button)的单击(click)事件,我们可以为其编写事件处理函数。

  • plus_handler 是一个匿名函数,它的参数是一个事件
  • 通过 buttononclick 属性,可以将按钮的单击事件与 plus_handler 进行绑定

打开浏览器,单击 +1 按钮,你将看到控制台输出:

状态管理

dioxus 是以状态(数据)驱动UI的。本案例中,我们的目标是:

  • 有一个计数器,初始值为 0
  • 当点击 +1 按钮时,该计数器的值加 1
  • 当点击 -1 按钮时 ,该计数器的值减 1
#[component]
fn CalcView() -> Element {
    let count = use_hook(|| 0);
    let plus_handler = move |e| info!("click {e:?}");
    rsx! {
        div { "当前计数:{count}" }
        div {
            button { "-1" }
            button { onclick: plus_handler, "+1" }
        }
    }
}
  • let count = use_hook(|| 0);我们使用 use_hook 来定义一个状态
    • 它的名字是 count
    • 它的初始值是 0
    • 注意:use_hook的参数是一个闭包
  • 在 rsx 中,使用 {} 语法,将该状态的值插入到 DOM 中: div { "当前计数:{count}" }

为了让按钮能够修改计数器状态的值,我们需要:

  • 将状态定义为 mut
  • 在事件处理函数中修改其值
#[component]
fn CalcView() -> Element {
    let mut count = use_hook(|| 0);
    let plus_handler = move |_| count = count + 1;
    rsx! {
        div { "当前计数:{count}" }
        div {
            button { "-1" }
            button { onclick: plus_handler, "+1" }
        }
    }
}

然而程序并未按我们预期工作。原因在于,use_hook 用错了,它的用法略显麻烦,我们改用 use_signal 代替:

#[component]
fn CalcView() -> Element {
    let mut count = use_signal(|| 0);
    let plus_handler = move |_| count.set(count + 1);
    let sub_handler = move |_| count.set(count - 1);
    rsx! {
        div { "当前计数:{count}" }
        div {
            button { onclick: sub_handler, "-1" }
            button { onclick: plus_handler, "+1" }
        }
    }
}

组件化

组件化是一个很好的编程实践,虽然我们将上面的 CalcView 进行组件化,将其拆分为子组件

// src/components.rs

#[component]
pub fn Info(count: Signal<i32>) -> Element {
    rsx! {
        div { "当前计数:{count}" }
    }
}

#[component]
pub fn Action(mut count: Signal<i32>) -> Element {
    let plus_handler = move |_| count.set(count + 1);
    let sub_handler = move |_| count.set(count - 1);
    rsx! {
        div {
            button { onclick: sub_handler, "-1" }
            button { onclick: plus_handler, "+1" }
        }
    }
}

#[component]
pub fn CalcView() -> Element {
    let count = use_signal(|| 0);

    rsx! {
        Info { count }
        Action { count }
    }
}

对应的,调用者也要进行修改:

// src/main.rs

use components::CalcView;
use dioxus::prelude::*;

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

mod components;

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

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

        CalcView {}
    }
}

Tailwind CSS

为了美化我们的应用,我们给计数器集成 Tailwind CSS。

首先,我们需要把我们的计数器项目初始化为 yarn 项目:

yarn init -y

然后,添加依赖:

接着,在项目的根目录创建 input.css,并复制以下内容:

@import "tailwindcss";

最后,保持以下命令运行:

npx @tailwindcss/cli -i ./input.css -o ./assets/main.css --watch

美化组件

// src/components.rs

#[component]
pub fn Info(count: Signal<i32>) -> Element {
    rsx! {
        div { class: "text-center text-2xl", "当前计数:{count}" }
    }
}

#[component]
pub fn Action(mut count: Signal<i32>) -> Element {
    let plus_handler = move |_| count.set(count + 1);
    let sub_handler = move |_| count.set(count - 1);
    rsx! {
        div { class: "flex justify-center items-center gap-x-4",
            button {
                class: "px-3 py-1 bg-gray-600 text-white rounded cursor-pointer hover:bg-gray-700",
                onclick: sub_handler,
                "-1"
            }
            button {
                class: "px-3 py-1 bg-blue-600 text-white rounded cursor-pointer hover:bg-blue-700",
                onclick: plus_handler,
                "+1"
            }
        }
    }
}

#[component]
pub fn CalcView() -> Element {
    let count = use_signal(|| 0);

    rsx! {
        div { class: "absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 p-6 rounded-lg border shadow-lg space-y-6 bg-gray-100 min-w-96",
            Info { count }
            Action { count }
        }
    }
}
要查看完整内容,请先登录