Rust跨平台桌面应用框架Dioxus-Desktop的使用,快速构建高性能原生GUI应用
Rust跨平台桌面应用框架Dioxus-Desktop的使用,快速构建高性能原生GUI应用
概述
dioxus-desktop
为Dioxus VirtualDom提供了一个基于webview的桌面渲染器。
该框架要求目标系统已安装webview。macOS和iOS设备默认已安装WebView,但Windows或Linux设备可能没有预装。要解决这些问题,请按照指南中的说明操作。
特性
- 简单的单行启动桌面应用程序
- 在原生线程上运行的Dioxus VirtualDom
- 通过
wry
和tao
提供完整的HTML/CSS支持 - 暴露了来自tao的
window
和Proxy
类型用于直接窗口操作 - 用于访问窗口、WebView和运行javascript的有用钩子
安装
在项目目录中运行以下Cargo命令:
cargo add dioxus-desktop
或在Cargo.toml中添加以下行:
dioxus-desktop = "0.6.3"
示例Demo
这里是一个完整的Dioxus-Desktop应用示例:
use dioxus::prelude::*;
use dioxus_desktop::{Config, WindowBuilder};
fn main() {
// 初始化Dioxus应用
dioxus_desktop::launch_cfg(
App, // 主组件
Config::new() // 配置
.with_window(
WindowBuilder::new() // 窗口构建器
.with_title("Dioxus Desktop Demo") // 设置标题
.with_resizable(true) // 允许调整大小
.with_inner_size(dioxus_desktop::LogicalSize::new(800, 600)), // 设置初始大小
),
);
}
// 定义主组件
fn App(cx: Scope) -> Element {
// 使用状态管理计数器
let count = use_state(&cx, || 0);
cx.render(rsx! {
div {
// 标题
h1 { "Dioxus Desktop Demo" }
// 计数器显示
p { "Count: {count}" }
// 增加按钮
button {
onclick: move |_| count.set(count + 1),
"Increment"
}
// 减少按钮
button {
onclick: move |_| count.set(count - 1),
"Decrement"
}
// 重置按钮
button {
onclick: move |_| count.set(0),
"Reset"
}
}
})
}
完整示例代码
基于上述内容,这里提供一个更完整的Dioxus-Desktop应用示例,包含更多UI元素和功能:
use dioxus::prelude::*;
use dioxus_desktop::{Config, WindowBuilder};
fn main() {
// 配置和启动Dioxus应用
dioxus_desktop::launch_cfg(
App,
Config::new()
.with_window(
WindowBuilder::new()
.with_title("Dioxus高级示例")
.with_resizable(true)
.with_inner_size(dioxus_desktop::LogicalSize::new(1024, 768))
.with_maximized(true),
),
);
}
fn App(cx: Scope) -> Element {
// 多个状态管理
let count = use_state(&cx, || 0);
let text = use_state(&cx, || String::new());
let dark_mode = use_state(&cx, || false);
cx.render(rsx! {
div {
// 根据暗黑模式切换样式
style: if *dark_mode.get() {
"background-color: #333; color: white;"
} else {
""
},
// 导航栏
nav {
h1 { "Dioxus高级示例" }
button {
onclick: move |_| dark_mode.set(!dark_mode.get()),
"切换主题"
}
}
// 计数器部分
section {
h2 { "计数器示例" }
p { "当前值: {count}" }
div {
button {
onclick: move |_| count.set(count + 1),
"+1"
}
button {
onclick: move |_| count.set(count - 1),
"-1"
}
button {
onclick: move |_| count.set(0),
"重置"
}
}
}
// 输入框部分
section {
h2 { "文本输入" }
input {
r#type: "text",
value: "{text}",
oninput: move |e| text.set(e.value.clone()),
placeholder: "输入一些文字..."
}
p { "你输入了: {text}" }
}
// 列表示例
section {
h2 { "动态列表" }
ul {
// 根据计数器值生成列表项
(0..*count.get()).map(|i| rsx! {
li { key: "{i}", "项目 #{i}" }
})
}
}
// 页脚
footer {
p { "© 2023 Dioxus示例应用" }
}
}
})
}
贡献
- 在我们的问题跟踪器上报告问题
- 加入discord并提出问题!
许可证
该项目采用MIT许可证授权。
除非您明确声明,否则您为Dioxus提交的任何贡献都将被视为MIT授权,无需任何附加条款或条件。
Rust跨平台桌面应用框架Dioxus-Desktop使用指南
Dioxus-Desktop是一个基于Rust的跨平台桌面应用框架,它允许开发者使用类似React的声明式语法构建高性能原生GUI应用。Dioxus支持Windows、macOS和Linux平台,并提供了热重载、虚拟DOM等现代前端开发特性。
核心特性
- 类似React的声明式UI语法
- 跨平台支持(Windows/macOS/Linux)
- 高性能虚拟DOM实现
- 内置状态管理
- 支持热重载
- 轻量级(打包后的应用体积小)
- 原生外观和性能
安装与设置
首先确保已安装Rust工具链,然后在Cargo.toml中添加dioxus依赖:
[dependencies]
dioxus = { version = "0.4", features = ["desktop"] }
基本使用示例
下面是一个简单的计数器应用示例:
use dioxus::prelude::*;
fn main() {
dioxus_desktop::launch(App);
}
fn App(cx: Scope) -> Element {
let mut count = use_state(cx, || 0);
cx.render(rsx! {
div {
h1 { "计数器: {count}" }
button { onclick: move |_| count += 1, "增加" }
button { onclick: move |_| count -= 1, "减少" }
}
})
}
组件化开发
Dioxus支持组件化开发,可以创建可复用的组件:
fn UserCard(cx: Scope, props: UserCardProps) -> Element {
cx.render(rsx! {
div {
class: "user-card",
h2 { "{props.name}" }
p { "年龄: {props.age}" }
p { "邮箱: {props.email}" }
}
})
}
#[derive(Props, PartialEq)]
struct UserCardProps {
name: String,
age: u8,
email: String,
}
状态管理
Dioxus提供了多种状态管理方式:
fn StateExample(cx: Scope) -> Element {
// 组件状态
let mut local_state = use_state(cx, || 0);
// 共享状态(跨组件)
let shared_state = use_shared_state::<AppState](cx).unwrap();
cx.render(rsx! {
div {
p { "本地状态: {local_state}" }
button { onclick: move |_| *local_state += 1, "增加本地状态" }
p { "共享状态: {shared_state.read().count}" }
button {
onclick: move |_| shared_state.write().count += 1,
"增加共享状态"
}
}
})
}
#[derive(Default)]
struct AppState {
count: i32,
}
样式处理
Dioxus支持多种样式处理方式:
fn StyledComponent(cx: Scope) -> Element {
cx.render(rsx! {
div {
// 内联样式
style: "background: #f0f0f0; padding: 20px; border-radius: 8px;",
h1 {
style: "color: #333; font-family: sans-serif;",
"带样式的组件"
}
button {
// 动态样式
style: "background: {if *count > 0 { "#4CAF50" } else { "#F44336" }};",
"状态按钮"
}
}
})
}
处理用户输入
fn InputExample(cx: Scope) -> Element {
let mut text = use_state(cx, || String::new());
cx.render(rsx! {
div {
input {
r#type: "text",
value: "{text}",
oninput: move |e| text.set(e.value.clone()),
placeholder: "输入一些文字..."
}
p { "你输入了: {text}" }
}
})
}
打包应用
要打包Dioxus桌面应用,可以使用以下命令:
cargo bundle --release
这将生成平台特定的安装包(如macOS的.app,Windows的.exe等)。
高级特性
- 自定义元素:可以创建自定义元素并注册到Dioxus中
- WebView集成:支持嵌入WebView内容
- 原生API调用:通过
tauri
或wry
可以调用原生API - 多窗口支持:可以创建和管理多个窗口
- 异步支持:完美集成Rust的async/await
性能优化技巧
- 使用
memoize
组件避免不必要的重新渲染 - 对于大型列表使用
VirtualScroll
组件 - 合理使用状态管理,避免全局状态过度使用
- 使用
should_render
生命周期方法控制组件更新
完整示例Demo
下面是一个完整的Dioxus-Desktop应用示例,结合了状态管理、组件化和样式处理:
use dioxus::prelude::*;
fn main() {
// 初始化共享状态
let shared_state = AppState::default();
// 启动应用
dioxus_desktop::launch_with_props(
App,
shared_state,
|c| c.with_window(|w| {
w.with_title("Dioxus示例应用")
.with_inner_size(dioxus::desktop::LogicalSize::new(800, 600))
})
);
}
// 应用主组件
fn App(cx: Scope<AppState>) -> Element {
// 本地状态
let mut local_counter = use_state(cx, || 0);
cx.render(rsx! {
div {
style: "font-family: sans-serif; padding: 20px;",
// 标题
h1 { style: "color: #2c3e50;", "Dioxus-Desktop示例应用" }
// 计数器组件
Counter {
count: *local_counter,
on_increment: move |_| local_counter += 1,
on_decrement: move |_| local_counter -= 1,
}
// 用户列表组件
UserList {}
// 输入组件
InputForm {}
}
})
}
// 计数器组件
#[inline_props]
fn Counter(cx: Scope, count: i32, on_increment: EventHandler, on_decrement: EventHandler) -> Element {
cx.render(rsx! {
div {
style: "background: #ecf0f1; padding: 15px; border-radius: 5px; margin-bottom: 20px;",
h2 { "计数器示例" }
p { "当前值: {count}" }
button {
style: "background: #3498db; color: white; border: none; padding: 8px 15px; margin-right: 10px; border-radius: 3px;",
onclick: move |e| on_increment.call(e),
"增加"
}
button {
style: "background: #e74c3c; color: white; border: none; padding: 8px 15px; border-radius: 3px;",
onclick: move |e| on_decrement.call(e),
"减少"
}
}
})
}
// 用户列表组件
fn UserList(cx: Scope) -> Element {
// 从共享状态获取用户数据
let users = use_shared_state::<AppState>(cx).unwrap();
cx.render(rsx! {
div {
style: "background: #ecf0f1; padding: 15px; border-radius: 5px; margin-bottom: 20px;",
h2 { "用户列表" }
// 用户列表
for user in users.read().users.iter() {
UserCard {
name: user.name.clone(),
age: user.age,
email: user.email.clone()
}
}
// 添加用户按钮
button {
style: "background: #2ecc71; color: white; border: none; padding: 8px 15px; margin-top: 10px; border-radius: 3px;",
onclick: move |_| {
let mut state = users.write();
let new_id = state.users.len() as u32 + 1;
state.users.push(User {
id: new_id,
name: format!("用户{}", new_id),
age: (new_id % 30) as u8 + 18,
email: format!("user{}@example.com", new_id)
});
},
"添加随机用户"
}
}
})
}
// 用户卡片组件
#[inline_props]
fn UserCard(cx: Scope, name: String, age: u8, email: String) -> Element {
cx.render(rsx! {
div {
style: "
background: white;
padding: 10px;
margin-bottom: 10px;
border-radius: 3px;
box-shadow: 0 2px 3px rgba(0,0,0,0.1);
",
h3 { style: "margin: 0 0 5px 0;", "{name}" }
p { style: "margin: 0; color: #7f8c8d;", "年龄: {age}" }
p { style: "margin: 0; color: #7f8c8d;", "邮箱: {email}" }
}
})
}
// 输入表单组件
fn InputForm(cx: Scope) -> Element {
let mut name = use_state(cx, || String::new());
let mut age = use_state(cx, || String::new());
let mut email = use_state(cx, || String::new());
let users = use_shared_state::<AppState>(cx).unwrap();
cx.render(rsx! {
div {
style: "background: #ecf0f1; padding: 15px; border-radius: 5px;",
h2 { "添加用户" }
div {
style: "margin-bottom: 10px;",
label { style: "display: block; margin-bottom: 5px;", "姓名:" }
input {
style: "width: 100%; padding: 8px; border: 1px solid #bdc3c7; border-radius: 3px;",
r#type: "text",
value: "{name}",
oninput: move |e| name.set(e.value.clone()),
placeholder: "输入姓名"
}
}
div {
style: "margin-bottom: 10px;",
label { style: "display: block; margin-bottom: 5px;", "年龄:" }
input {
style: "width: 100%; padding: 8px; border: 1px solid #bdc3c7; border-radius: 3px;",
r#type: "number",
value: "{age}",
oninput: move |e| age.set(e.value.clone()),
placeholder: "输入年龄"
}
}
div {
style: "margin-bottom: 10px;",
label { style: "display: block; margin-bottom: 5px;", "邮箱:" }
input {
style: "width: 100%; padding: 8px; border: 1px solid #bdc3c7; border-radius: 3px;",
r#type: "email",
value: "{email}",
oninput: move |e| email.set(e.value.clone()),
placeholder: "输入邮箱"
}
}
button {
style: "background: #2ecc71; color: white; border: none; padding: 8px 15px; border-radius: 3px;",
onclick: move |_| {
if !name.is_empty() && !age.is_empty() && !email.is_empty() {
let mut state = users.write();
let new_id = state.users.len() as u32 + 1;
state.users.push(User {
id: new_id,
name: name.to_string(),
age: age.parse().unwrap_or(18),
email: email.to_string()
});
name.set(String::new());
age.set(String::new());
email.set(String::new());
}
},
"添加用户"
}
}
})
}
// 应用状态
#[derive(Default)]
struct AppState {
count: i32,
users: Vec<User>,
}
// 用户结构体
#[derive(Clone)]
struct User {
id: u32,
name: String,
age: u8,
email: String,
}
这个完整示例展示了Dioxus-Desktop的核心功能:
- 组件化开发 - 将应用拆分为多个可复用组件
- 状态管理 - 同时使用本地状态和共享状态
- 样式处理 - 内联样式和动态样式
- 用户输入处理 - 表单输入和事件处理
- 列表渲染 - 动态渲染用户列表
Dioxus-Desktop结合了Rust的性能优势和现代前端开发的便利性,是构建跨平台桌面应用的优秀选择。