Rust前端开发框架Dioxus的使用,dioxus-lib提供跨平台UI构建与高效渲染方案
Rust前端开发框架Dioxus的使用,dioxus-lib提供跨平台UI构建与高效渲染方案
Dioxus是一个用Rust构建的跨平台用户界面框架,可以快速开发复杂的用户界面。任何Dioxus应用都可以在Web浏览器、桌面应用、移动应用中运行。
主要特点
-
受React启发,支持类似的概念:
- 状态管理钩子
- VirtualDom和差异比较
- 并发、纤程和异步渲染
- JSX风格的模板语法
-
性能优越:比许多其他Rust UI库(Yew/Percy)性能更好,比React性能更优
基本使用
简单示例
use dioxus::prelude::*;
fn main() {
dioxus::launch(App);
}
#[component]
fn App() -> Element {
rsx! { "hello world!" }
}
元素和组件
let value = "123";
rsx! {
div {
class: "my-class {value}", // 属性
onclick: move |_| info!("clicked!"), // 监听器
h1 { "hello world" }, // 子元素
}
}
循环和条件
rsx! {
div {
for _ in 0..10 {
span { "hello world" }
}
}
}
带属性的组件
#[derive(Props, PartialEq)]
struct HeaderProps {
title: String,
color: String,
}
#[component]
fn Header(props: HeaderProps) -> Element {
rsx! {
div {
background_color: "{props.color}"
h1 { "{props.title}" }
}
}
}
状态管理
#[component]
fn App() -> Element {
let mut count = use_signal(|| 0);
rsx!(
div { "Count: {count}" }
button { onclick: move |_| count += 1, "Increment" }
button { onclick: move |_| count -= 1, "Decrement" }
)
}
完整示例
这是一个完整的计数器应用示例:
use dioxus::prelude::*;
fn main() {
dioxus::launch(App);
}
#[component]
fn App() -> Element {
let mut count = use_signal(|| 0);
rsx! {
div {
h1 { "计数器示例" }
div {
class: "counter-display",
"当前值: {count}"
}
div {
class: "button-group",
button {
class: "increment-btn",
onclick: move |_| count += 1,
"增加"
}
button {
class: "decrement-btn",
onclick: move |_| count -= 1,
"减少"
}
button {
class: "reset-btn",
onclick: move |_| count.set(0),
"重置"
}
}
}
}
}
跨平台支持
Dioxus支持构建:
- Web应用
- 桌面应用
- 移动应用
- 服务器端渲染
- 预渲染和重新hydration
安装
在Cargo.toml中添加依赖:
dioxus-lib = "0.6.2"
或者运行命令:
cargo add dioxus-lib
Dioxus提供了高效、灵活的方式来构建跨平台用户界面,结合了Rust的性能优势与现代UI开发的便利性。
1 回复
Rust前端开发框架Dioxus使用指南
Dioxus简介
Dioxus是一个基于Rust的声明式UI框架,用于构建跨平台用户界面。它受到React的启发,提供了类似的组件化开发体验,同时利用Rust的性能优势。
Dioxus-lib是该框架的核心库,支持:
- Web平台(编译为WebAssembly)
- 桌面应用(通过Tauri或Wry)
- 移动应用(实验性支持)
- 服务端渲染
安装与设置
首先在Cargo.toml中添加依赖:
[dependencies]
dioxus = "0.4" # 请检查最新版本
dioxus-web = "0.4" # 用于Web目标
基本使用
创建简单组件
use dioxus::prelude::*;
fn App(cx: Scope) -> Element {
cx.render(rsx! {
div {
h1 { "欢迎使用Dioxus!" }
Counter {}
}
})
}
fn Counter(cx: Scope) -> Element {
let mut count = use_state(cx, || 0);
cx.render(rsx! {
div {
p { "当前计数: {count}" }
button { onclick: move |_| count += 1, "增加" }
button { onclick: move |_| count -= 1, "减少" }
}
})
}
启动应用
对于Web目标:
fn main() {
dioxus_web::launch(App);
}
对于桌面应用(需要额外配置):
fn main() {
dioxus_desktop::launch_cfg(App, |c| c.with_window(|w| w.with_title("我的应用")));
}
核心概念
1. RSX语法
Dioxus使用类似JSX的RSX语法:
rsx! {
div {
class: "my-class",
id: "my-id",
h1 { "标题" }
p { "段落内容" }
for item in items.iter() {
ItemComponent { key: "{item.id}", item: item }
}
}
}
2. 状态管理
fn StatefulComponent(cx: Scope) -> Element {
let mut text = use_state(cx, || String::new());
cx.render(rsx! {
div {
input {
value: "{text}",
oninput: move |e| text.set(e.value.clone())
}
p { "你输入了: {text}" }
}
})
}
3. 事件处理
fn EventHandling(cx: Scope) -> Element {
cx.render(rsx! {
button {
onclick: move |_| println!("按钮被点击!"),
onmouseenter: move |_| println!("鼠标进入"),
"点击我"
}
})
}
高级特性
跨平台组件
#[derive(Props, PartialEq)]
struct UserCardProps {
name: String,
age: u8,
}
fn UserCard(cx: Scope<UserCardProps>) -> Element {
cx.render(rsx! {
div {
class: "user-card",
h2 { "{cx.props.name}" }
p { "年龄: {cx.props.age}" }
}
})
}
条件渲染
fn ConditionalRendering(cx: Scope) -> Element {
let show = use_state(cx, || true);
cx.render(rsx! {
div {
button { onclick: move |_| show.set(!show), "切换" }
if *show {
p { "现在你看到我了!" }
} else {
p { "现在你看不到我了" }
}
}
})
}
列表渲染
fn ListRendering(cx: Scope) -> Element {
let items = use_state(cx, || vec!["苹果", "香蕉", "橙子"]);
cx.render(rsx! {
ul {
for item in items.iter() {
li { key: "{item}", "{item}" }
}
}
})
}
性能优化
Dioxus通过以下方式实现高效渲染:
- 虚拟DOM差异算法
- 记忆化组件
- 细粒度状态更新
// 使用memo优化组件
fn OptimizedComponent(cx: Scope) -> Element {
let count = use_state(cx, || 0);
cx.render(rsx! {
div {
ExpensiveComponent { count: count }
button { onclick: move |_| count += 1, "增加" }
}
})
}
#[inline_props]
fn ExpensiveComponent(cx: Scope, count: &i32) -> Element {
// 这个组件只在count变化时重新渲染
cx.render(rsx! {
p { "计数: {count}" }
})
}
跨平台开发
Dioxus支持多种平台目标:
Web目标
// 使用dioxus-web
fn main() {
dioxus_web::launch(App);
}
桌面目标
// 使用dioxus-desktop
fn main() {
dioxus_desktop::launch(App);
}
静态站点生成
// 使用dioxus-ssr
fn main() {
let html = dioxus_ssr::render_lazy(App);
// 保存到文件或作为服务器响应
}
生态系统
Dioxus提供了一系列周边库:
dioxus-router
:路由解决方案dioxus-use
:常用hooks集合dioxus-material
:Material UI组件dioxus-bootstrap
:Bootstrap样式组件
完整示例:Todo应用
下面是内容中提供的完整Todo应用示例:
use dioxus::prelude::*;
fn main() {
dioxus_web::launch(App);
}
#[derive(Clone, Debug)]
struct TodoItem {
id: usize,
title: String,
completed: bool,
}
fn App(cx: Scope) -> Element {
let todos = use_ref(cx, || vec![
TodoItem { id: 1, title: "学习Rust".to_string(), completed: false },
TodoItem { id: 2, title: "尝试Dioxus".to_string(), completed: true },
]);
let new_todo = use_state(cx, || String::new());
cx.render(rsx! {
div {
h1 { "Todo应用" }
// 添加新todo
div {
input {
value: "{new_todo}",
oninput: move |e| new_todo.set(e.value.clone()),
placeholder: "输入新任务"
}
button {
onclick: move |_| {
if !new_todo.is_empty() {
let mut todos = todos.write();
todos.push(TodoItem {
id: todos.len() + 1,
title: new_todo.get().clone(),
completed: false,
});
new_todo.set(String::new());
}
},
"添加"
}
}
// 显示todo列表
ul {
for todo in todos.read().iter() {
li {
key: "{todo.id}",
input {
r#type: "checkbox",
checked: todo.completed,
onchange: move |e| {
let mut todos = todos.write();
if let Some(t) = todos.iter_mut().find(|t| t.id == todo.id) {
t.completed = e.value.parse().unwrap();
}
}
}
span {
style: if todo.completed { "text-decoration: line-through" } else { "" },
"{todo.title}"
}
button {
onclick: move |_| {
todos.write().retain(|t| t.id != todo.id);
},
"删除"
}
}
}
}
}
})
}
扩展示例:增强版Todo应用
以下是一个增强版的Todo应用示例,包含更多功能:
use dioxus::prelude::*;
fn main() {
dioxus_web::launch(App);
}
#[derive(Clone, Debug)]
struct TodoItem {
id: usize,
title: String,
completed: bool,
created_at: chrono::DateTime<chrono::Local>,
}
fn App(cx: Scope) -> Element {
let todos = use_ref(cx, || {
vec![
TodoItem {
id: 1,
title: "学习Rust".to_string(),
completed: false,
created_at: chrono::Local::now(),
},
TodoItem {
id: 2,
title: "尝试Dioxus".to_string(),
completed: true,
created_at: chrono::Local::now(),
},
]
});
let new_todo = use_state(cx, || String::new());
let filter = use_state(cx, || "all".to_string());
cx.render(rsx! {
div {
style: "max-width: 600px; margin: 0 auto; padding: 20px;",
h1 { style: "text-align: center;", "Todo应用" }
// 过滤选项
div {
style: "display: flex; gap: 10px; margin-bottom: 20px;",
button {
onclick: move |_| filter.set("all".to_string()),
style: "padding: 5px 10px;",
"全部"
}
button {
onclick: move |_| filter.set("active".to_string()),
style: "padding: 5px 10px;",
"未完成"
}
button {
onclick: move |_| filter.set("completed".to_string()),
style: "padding: 5px 10px;",
"已完成"
}
}
// 添加新todo
div {
style: "display: flex; gap: 10px; margin-bottom: 20px;",
input {
style: "flex-grow: 1; padding: 8px;",
value: "{new_todo}",
oninput: move |e| new_todo.set(e.value.clone()),
placeholder: "输入新任务",
onkeydown: move |e| {
if e.key == "Enter" && !new_todo.is_empty() {
let mut todos = todos.write();
todos.push(TodoItem {
id: todos.len() + 1,
title: new_todo.get().clone(),
completed: false,
created_at: chrono::Local::now(),
});
new_todo.set(String::new());
}
}
}
button {
style: "padding: 8px 16px;",
onclick: move |_| {
if !new_todo.is_empty() {
let mut todos = todos.write();
todos.push(TodoItem {
id: todos.len() + 1,
title: new_todo.get().clone(),
completed: false,
created_at: chrono::Local::now(),
});
new_todo.set(String::new());
}
},
"添加"
}
}
// 显示todo列表
ul {
style: "list-style: none; padding: 0;",
for todo in todos.read().iter().filter(|todo| match filter.get().as_str() {
"all" => true,
"active" => !todo.completed,
"completed" => todo.completed,
_ => true,
}) {
li {
key: "{todo.id}",
style: "display: flex; align-items: center; gap: 10px; padding: 10px; border-bottom: 1px solid #eee;",
input {
r#type: "checkbox",
checked: todo.completed,
onchange: move |e| {
let mut todos = todos.write();
if let Some(t) = todos.iter_mut().find(|t| t.id == todo.id) {
t.completed = e.value.parse().unwrap();
}
}
}
div {
style: "flex-grow: 1;",
span {
style: if todo.completed { "text-decoration: line-through; color: #999;" } else { "" },
"{todo.title}"
}
div {
style: "font-size: 0.8em; color: #666;",
"创建于: {todo.created_at.format("%Y-%m-%d %H:%M")}"
}
}
button {
style: "padding: 5px 10px;",
onclick: move |_| {
todos.write().retain(|t| t.id != todo.id);
},
"删除"
}
}
}
}
// 统计信息
div {
style: "margin-top: 20px; text-align: right; color: #666;",
"总计: {todos.read().len()} | ",
"已完成: {todos.read().iter().filter(|t| t.completed).count()} | ",
"未完成: {todos.read().iter().filter(|t| !t.completed).count()}"
}
}
})
}
Dioxus为Rust开发者提供了一种现代化的UI开发方式,结合了Rust的性能优势与声明式UI的开发效率,是构建跨平台应用的优秀选择。