Rust跨平台桌面应用框架Dioxus-Desktop的使用,快速构建高性能原生GUI应用

Rust跨平台桌面应用框架Dioxus-Desktop的使用,快速构建高性能原生GUI应用

概述

dioxus-desktop 为Dioxus VirtualDom提供了一个基于webview的桌面渲染器。

该框架要求目标系统已安装webview。macOS和iOS设备默认已安装WebView,但Windows或Linux设备可能没有预装。要解决这些问题,请按照指南中的说明操作。

特性

  • 简单的单行启动桌面应用程序
  • 在原生线程上运行的Dioxus VirtualDom
  • 通过wrytao提供完整的HTML/CSS支持
  • 暴露了来自tao的windowProxy类型用于直接窗口操作
  • 用于访问窗口、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授权,无需任何附加条款或条件。


1 回复

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等)。

高级特性

  1. 自定义元素:可以创建自定义元素并注册到Dioxus中
  2. WebView集成:支持嵌入WebView内容
  3. 原生API调用:通过tauriwry可以调用原生API
  4. 多窗口支持:可以创建和管理多个窗口
  5. 异步支持:完美集成Rust的async/await

性能优化技巧

  1. 使用memoize组件避免不必要的重新渲染
  2. 对于大型列表使用VirtualScroll组件
  3. 合理使用状态管理,避免全局状态过度使用
  4. 使用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的核心功能:

  1. 组件化开发 - 将应用拆分为多个可复用组件
  2. 状态管理 - 同时使用本地状态和共享状态
  3. 样式处理 - 内联样式和动态样式
  4. 用户输入处理 - 表单输入和事件处理
  5. 列表渲染 - 动态渲染用户列表

Dioxus-Desktop结合了Rust的性能优势和现代前端开发的便利性,是构建跨平台桌面应用的优秀选择。

回到顶部