- 支持试读
开启 dioxus 之旅
本章将讨论 dioxus 的安装、项目创建、运行和构建。 - 支持试读
dioxus 组件与 rsx
本章将开始第一个案例:计数器的编写。首先,我们需要学习 dioxus 组件的编写和 rsx 语法。 - 支持试读
计数器
本章我们将开始编写计数器。你将学习到:事件处理、状态管理、组件通讯、集成 Tailwind CSS 等知识。 - 支持试读
博客
本章我们将实现博客,你将学习到:dioxus 路由、获取远程数据、条件渲染、列表渲染等知识。 全局状态
本章我们开始最后一个案例:阅后即焚的开发。在真正进行开发之前,我们还需要掌握一些基础知识,本章我们将学习 dioxus 的全局状态。布局与嵌套路由
本章我们将讨论 dioxus 的布局与嵌套路由。表单处理
本章我们将学习 dioxus 的表单处理。和 React 类似,dioxus 也分为受控表单和非受控表单。阅后即焚前台UI
本章我们开始编写阅后即焚的前台UI。- 支持试读
阅后即焚API
本章我们使用 AXUM 开发阅后即焚的前台 API。 整合阅后即焚前端和API
本章将对阅后即焚前端和API进行整合。- 支持试读
编译和部署
本章我们将讨论 dioxus web 的编译和部署到 NGINX。 【加餐】dioxus 服务端渲染
本章使用前文的『博客』中的用户数据为案例,来讲解 dioxus 服务端渲染。
计数器
- 8
- 2025-04-30 21:18:29
本章我们将开始编写计数器。你将学习到:事件处理、状态管理、组件通讯、集成 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
是一个匿名函数,它的参数是一个事件- 通过
button
的onclick
属性,可以将按钮的单击事件与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 }
}
}
}