Rust全栈开发框架Dioxus-Fullstack的使用:构建高性能跨平台Web应用与桌面程序

Rust全栈开发框架Dioxus-Fullstack的使用:构建高性能跨平台Web应用与桌面程序

Dioxus Fullstack

Dioxus框架的全栈工具。

特性

  • 与Axum服务器框架集成,提供服务和渲染Dioxus应用的实用工具
  • 服务器功能允许您从客户端调用服务器上的代码,就像调用普通函数一样
  • 使用dioxus-hot-reload实现即时RSX热重载
  • 从服务器向客户端传递根属性

示例

不到30行代码实现全栈Dioxus

#![allow(non_snake_case)]
use dioxus::prelude::*;

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

#[component]
fn App() -> Element {
    let mut meaning = use_signal(|| None);

    rsx! {
        h1 { "Meaning of life: {meaning:?}" }
        button {
            onclick: move |_| async move {
                if let Ok(data) = get_meaning("life the universe and everything".into()).await {
                    meaning.set(data);
                }
            },
            "Run a server function"
        }
    }
}

#[server]
async fn get_meaning(of: String) -> Result<Option<u32>, ServerFnError> {
    Ok(of.contains("life").then(|| 42))
}

Axum集成

如果您已有Axum路由器或需要更多服务器控制,可以使用DioxusRouterExt特性与现有Axum路由器集成。

首先,确保您的axum依赖是可选的,并通过服务器特性标志启用:

[dependencies]
dioxus = { version = "*", features = ["fullstack"] }
axum = { version = "0.7.0", optional = true }
tokio = { version = "1.0", features = ["full"], optional = true }
dioxus-cli-config = { version = "*", optional = true }

[features]
server = ["dioxus/server", "dep:axum", "dep:tokio", "dioxus-cli-config"]
web = ["dioxus/web"]

然后我们可以设置带有axum服务器的dioxus:

#![allow(non_snake_case)]
use dioxus::prelude::*;

// 服务器的入口点
#[cfg(feature = "server")]
#[tokio::main]
async fn main() {
    // 获取服务器应运行的地址
    let address = dioxus::cli_config::fullstack_address_or_localhost();

    // 设置axum路由器
    let router = axum::Router::new()
        // 使用`serve_dioxus_application`方法将dioxus应用程序添加到路由器
        .serve_dioxus_application(ServeConfigBuilder::default(), App);

    // 最后启动服务器
    let router = router.into_make_service();
    let listener = tokio::net::TcpListener::bind(address).await.unwrap();
    axum::serve(listener, router).await.unwrap();
}

// 对于其他平台,只需启动应用程序
#[cfg(not(feature = "server"))]
fn main() {
    dioxus::launch(App);
}

#[component]
fn App() -> Element {
    let mut meaning = use_signal(|| None);

    rsx! {
        h1 { "Meaning of life: {meaning:?}" }
        button {
            onclick: move |_| async move {
                if let Ok(data) = get_meaning("life the universe and everything"].into()).await {
                    meaning.set(data);
                }
            },
            "Run a server function"
        }
    }
}

#[server]
async fn get_meaning(of: String) -> Result<Option<u32>, ServerFnError> {
    Ok(of.contains("life").then(|| 42))
}

完整示例Demo

以下是一个完整的Dioxus-Fullstack示例,包含客户端和服务器端代码:

// Cargo.toml
[package]
name = "dioxus-fullstack-demo"
version = "0.1.0"
edition = "2021"

[dependencies]
dioxus = { version = "0.6", features = ["fullstack"] }
axum = { version = "0.7", optional = true }
tokio = { version = "1.0", features = ["full"], optional = true }
dioxus-cli-config = { version = "0.6", optional = true }
serde = { version = "1.0", features = ["derive"] }

[features]
server = ["dioxus/server", "dep:axum", "dep:tokio", "dioxus-cli-config"]
web = ["dioxus/web"]
// src/main.rs
#![allow(non_snake_case)]
use dioxus::prelude::*;

// 服务器入口点
#[cfg(feature = "server")]
#[tokio::main]
async fn main() {
    use axum::Router;
    use dioxus_fullstack::prelude::*;
    
    let addr = dioxus::cli_config::fullstack_address_or_localhost();
    println!("Server running on: {}", addr);
    
    let router = Router::new()
        .serve_dioxus_application(ServeConfigBuilder::default(), App);
    
    let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
    axum::serve(listener, router.into_make_service())
        .await
        .unwrap();
}

// 客户端入口点
#[cfg(not(feature = "server"))]
fn main() {
    dioxus::launch(App);
}

#[component]
fn App() -> Element {
    let mut count = use_signal(|| 0);
    let mut server_data = use_signal(|| None);

    rsx! {
        h1 { "Dioxus Fullstack Demo" }
        
        div {
            h2 { "Client State: {count}" }
            button {
                onclick: move |_| count += 1,
                "Increment"
            }
            button {
                onclick: move |_| count -= 1,
                "Decrement"
            }
        }
        
        div {
            h2 { "Server Interaction" }
            button {
                onclick: move |_| async move {
                    if let Ok(data) = fetch_data(count()).await {
                        server_data.set(Some(data));
                    }
                },
                "Fetch from Server"
            }
            
            if let Some(data) = server_data() {
                p { "Server response: {data}" }
            }
        }
    }
}

#[server]
async fn fetch_data(client_count: i32) -> Result<String, ServerFnError> {
    // 模拟一些服务器处理
    tokio::time::sleep(std::time::Duration::from_secs(1)).await;
    
    // 返回处理后的数据
    Ok(format!("Server processed your count: {}", client_count * 2))
}

开始使用

要开始使用全栈Dioxus,请查看入门指南或完整堆栈示例。

贡献

  • 在问题跟踪器上报告问题
  • 加入discord并提问

许可证

本项目根据MIT许可证授权。


1 回复

Rust全栈开发框架Dioxus-Fullstack的使用:构建高性能跨平台Web应用与桌面程序

Dioxus-Fullstack是一个基于Rust的全栈开发框架,允许开发者使用同一套代码构建Web应用和桌面程序。它结合了前端框架Dioxus和后端能力,提供了完整的全栈开发体验。

核心特性

  • 跨平台支持:一套代码可编译为Web应用、桌面应用(Windows/macOS/Linux)和移动端应用
  • 高性能:得益于Rust的零成本抽象和高效内存管理
  • 响应式UI:类似React的声明式组件模型
  • 全栈能力:内置前后端通信机制
  • 热重载:开发时快速迭代

安装与设置

首先确保已安装Rust工具链,然后添加Dioxus依赖:

# Cargo.toml
[dependencies]
dioxus = { version = "0.4", features = ["fullstack"] }
dioxus-fullstack = "0.4"

基础使用示例

1. 创建基本应用

use dioxus::prelude::*;

fn App(cx: Scope) -> Element {
    cx.render(rsx! {
        div {
            h1 { "欢迎使用Dioxus-Fullstack!" }
            p { "这是一个全栈Rust应用" }
            Counter {}
        }
    })
}

#[component]
fn Counter(cx: Scope) -> Element {
    let mut count = use_state(cx, || 0);
    
    cx.render(rsx! {
        div {
            button { onclick: move |_| count += 1, "增加" }
            button { onclick: move |_| count -= 1, "减少" }
            p { "当前计数: {count}" }
        }
    })
}

2. 全栈通信示例

前端代码 (client.rs):

use dioxus::prelude::*;
use dioxus_fullstack::prelude::*;

fn App(cx: Scope) -> Element {
    let response = use_future(cx, (), |_| async move {
        get_server_data().await
    });
    
    cx.render(rsx! {
        div {
            h1 { "服务器数据" }
            match response.value() {
                Some(Ok(data)) => rsx! { p { "服务器说: {data}" } },
                Some(Err(err)) => rsx! { p { "错误: {err}" } },
                None => rsx! { p { "加载中..." } },
            }
        }
    })
}

#[server]
async fn get_server_data() -> Result<String, ServerFnError> {
    Ok("你好,来自服务器的消息!".to_string())
}

后端代码 (server.rs):

use dioxus_fullstack::prelude::*;

#[tokio::main]
async fn main() {
    let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080));
    
    axum::Server::bind(&addr)
        .serve(
            app()
                .into_make_service()
        )
        .await
        .unwrap();
}

fn app() -> Router {
    Router::new()
        .serve_dioxus_application("", ServeConfig::builder().build())
        .register_server_fns()
}

构建与运行

开发模式

# 运行全栈开发服务器
dx serve --hot-reload

生产构建

# 构建Web应用
dx build --release --target web

# 构建桌面应用
dx build --release --target desktop

桌面应用打包

Dioxus-Fullstack支持使用Tauri打包桌面应用:

# Cargo.toml
[dependencies]
tauri = { version = "1.0", features = [] }

然后运行:

cargo tauri build

高级功能

1. 状态管理

use dioxus::prelude::*;
use dioxus_fullstack::prelude::*;

#[derive(Clone, Routable)]
enum Route {
    #[route("/")]
    Home {},
    #[route("/about")]
    About {},
}

fn App(cx: Scope) -> Element {
    cx.render(rsx! {
        Router::<Route> {}
    })
}

#[component]
fn Home(cx: Scope) -> Element {
    cx.render(rsx! { h1 { "首页" } })
}

#[component]
fn About(cx: Scope) -
回到顶部