目录

Dioxus后端代码

Dioxus后端代码

Dioxus 不只是个“前端框架”,它是个全栈框架——意思是:前端 + 后端,它都能写,还住一起,不拆家!

但默认情况下,它只当“前端小弟”。要想让它变成“全栈大佬”,得先在 Cargo.toml打开开关

修改依赖:开启“全栈模式”

[dependencies]
dioxus = { version = "0.6.0", features = ["fullstack"] }

再加一个“后门入口”:server 功能

[features]
default = []           # 关掉默认只跑前端
web = ["dioxus/web"]
desktop = ["dioxus/desktop"]
mobile = ["dioxus/mobile"]
server = ["dioxus/server"]  # 加上这句:后端也能跑!

如果你创建项目时选了“yes,我要全栈”,那这步已经自动完成了!

注意:改完这些,重启服务!
因为 dx serve 不会自动热重载这个配置。

运行时要加平台参数:

dx serve --platform web

等它重新编译完,你会看到控制台显示:

16:35:41 [dev] Build completed successfully in 167722ms, launching app! 💫
╭────────────────────────────────────────────────────────────────────────────────────────── /:more ╮
│  App:     ━━━━━━━━━━━━━━━━━━━━━━━━━━  🎉 178.0s    Platform: Web + fullstack                     │
│  Server:  ━━━━━━━━━━━━━━━━━━━━━━━━━━  🎉 178.0s    App features: ["web"]│  Status:  Serving hot_dog 🚀 186.1s                Serving at: http://127.0.0.1:8080             │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯

第二步:用“服务器函数”——前后端的“心灵感应”

Dioxus 提供了一个超爽的功能:服务器函数(Server Functions)
你可以把它理解为:

写一个函数,前后端自动打通,像魔法一样!

你写一个函数,标个 #[server]
它就能在前端调用,在后端执行,中间的网络通信、数据打包,全给你包了!

举个栗子:收藏一只狗

#[server]
async fn save_dog(image: String) -> Result<(), ServerFnError> {
    Ok(())
}

就这么简单!你写的是一个普通函数,但 Dioxus 暗地里做了三件事:

  1. 在前端:变成一个 reqwest 请求,发给后端
  2. 在后端:变成一个 axum 接口,接收请求
  3. 自动注册:启动时自动挂载到路由上,比如 /api/save_dog

你可以把它想象成“远程遥控器”:
前端按按钮,后端执行任务,中间不用你接电线。


前后端是怎么“分家过日子”的?

虽然住一起,但前后端还是两个独立程序

  • 前端:跑在浏览器里,用 --feature web 编译
  • 后端:跑在服务器上,用 --feature server 编译

这就意味着:

不能把后端的秘密暴露给前端!

比如,你不能这样写:

//  危险!密码会被打包进前端代码!
static DB_PASSWORD: &str = "123456";

#[server]
async fn save_dog(image: String) -> Result<(), ServerFnError> {
    connect_to_db(DB_PASSWORD); // 前端也能看到这个密码!
    Ok(())
}

正确做法:把敏感代码放进 #[cfg(feature = "server")] 里:

#[cfg(feature = "server")]
mod server_utils {
    pub static DB_PASSWORD: &str = "123456";
}

#[server]
async fn save_dog(image: String) -> Result<(), ServerFnError> {
    let password = server_utils::DB_PASSWORD;
    connect_to_db(password);
    Ok(())
}

这样,server_utils 只在后端编译,前端完全看不到!

第三步:管理依赖——谁该上车,谁该下车?

有些库只能在服务器上跑,比如 tokiostd::fs
有些只能在浏览器跑,比如 web-sys

为了避免“张飞穿针”——(服务器代码跑到浏览器里去执行)
我们要用 optional = true 控制依赖:

[dependencies]
tokio = { version = "1", optional = true }

[features]
server = ["dioxus/server","dep:tokio"]  # 只有开启 server 功能时,才引入 tokio

这样,前端打包时,tokio 根本不会被打进去!

给“潮狗”加个“收藏本”!

现在,我们来实现真正的“收藏”功能:把用户喜欢的狗图链接,存到一个叫 dogs.txt 的文件里。

生产环境当然要用数据库,但咱们先用文件练手!

#[server]
async fn save_dog(image: String) -> Result<(), ServerFnError> {
    use std::io::Write;

    // 打开 dogs.txt,没有就创建,有就追加
    let mut file = std::fs::OpenOptions::new()
        .write(true)
        .append(true)
        .create(true)
        .open("dogs.txt")
        .unwrap();

    // 把狗图链接写进去,加个换行
    writeln!(file, "{}", image).unwrap();

    Ok(())
}

前端调用:一键收藏!

回到前端,我们改一下“收藏”按钮:

button {
    id: "save",
    onclick: move |_| async move {
        let current = img_src.cloned().unwrap(); // 拿到当前狗图
        img_src.restart();                       // 换下一只
        let _ = save_dog(current).await;         // 调用服务器函数!
    },
    "收藏!"
}

效果:

  1. 点“收藏”
  2. 当前狗图链接传给后端
  3. 后端写入 dogs.txt
  4. 页面自动换狗

从此,你的“心动小狗”都有地方安家了!

完整代码

use dioxus::prelude::*;

static CSS: Asset = asset!("/assets/main.css");

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

#[derive(Clone)]
struct TitleState(String);

#[component]
fn App() -> Element {
    use_context_provider(|| TitleState("HotDog".to_string()));
    rsx! {
        document::Stylesheet {href:CSS}
        Title {  }
        DogView {  }
    }
}

#[component]
fn Title() -> Element {
    let title = use_context::<TitleState>();
    rsx! {
        div { id: "title",
            h1 { "{title.0}" }
        }
    }
}

#[derive(serde::Deserialize)]
struct DogApi {
    message: String,
}

#[component]
fn DogView() -> Element {
    // 用use_resource 创建一个“资源”,他会自动跑这个异步任务
    let mut img_src = use_resource(|| async move {
        reqwest::get("https://dog.ceo/api/breeds/image/random")
            .await
            .unwrap()
            .json::<DogApi>()
            .await
            .unwrap()
            .message
    });

    rsx! {
        div { id: "dogview",
            img { src: img_src.cloned().unwrap_or_default() }
        }

        div { id: "buttons",
            button { onclick: move  |_| img_src.restart(), id: "skip", "skip!" }
            button { onclick: move  |_| async move{
                let current = img_src.cloned().unwrap();
                img_src.restart();
                _= save_dog(current).await;
            }, id: "save", "save!" }
        }
    }
}

#[server]
async fn save_dog(image: String) -> Result<(), ServerFnError> {
    use std::io::Write; //这一行很重要
    let mut file = std::fs::OpenOptions::new()
        .write(true)
        .append(true)
        .create(true)
        .open("dogs.txt")
        .unwrap();
    writeln!(file, "{}", image).unwrap();
    Ok(())
}

全栈开发的“三板斧”

武器作用类比
fullstack feature打开全栈模式给房子通水电
#[server] 函数前后端通信心灵感应遥控器
cfg(feature = "server")隔离后端代码保险柜,只给后端看

从记事本升级到数据库!

今天,我们要给它升级——装一个真正的“狗窝”:数据库

ID狗图链接(url)
1
2
3

它不仅能存数据,还能:

  • 快速查找
  • 防止重复
  • 自动编号
  • 不怕断电
  • 多人同时用也不打架

比记事本强一万倍!


选个啥数据库?——先看你是“独居”还是“合租”

市面上数据库五花八门,挑花眼?
别急,我们按场景分类:

1. 独居党(单用户) → 用 SQLite
  • 就一个文件,像U盘一样塞进App
  • 不用装服务,不用配服务器
  • 轻量、可靠、零配置
  • 适合你自己的小项目

HotDog 就是“独居狗”,所以选 SQLite!

2. 合租党(多用户) → 用 PostgreSQL / MySQL
  • 支持多人同时访问
  • 功能强大,插件多
  • 适合正式上线的App
其他“特种兵”数据库:
  • Redis:超快“记忆闪存”,适合存临时数据(比如验证码)
  • MongoDB:不规则数据的家,比如用户自定义表单
  • SurrealDB:新晋“六边形战士”,啥都能干
  • CockroachDB:分布式“永不断电”数据库

一句话总结:
大部分项目,PostgreSQL 或 MySQL 准没错。
小项目、个人玩具?SQLite 是你的最佳拍档!

给 HotDog 装上 SQLite 引擎!

现在,我们动手给“潮狗”换心脏!

第一步:加依赖 —— 把“数据库司机”请上车

我们要用 rusqlite 这个 crate,它是 Rust 里操作 SQLite 的“老司机”。

但注意:数据库操作只能在后端运行!
所以我们要把它“锁”在 server 功能里。

[dependencies]
rusqlite = { version = "0.32.1", optional = true }

[features]
server = ["dioxus/server", "dep:rusqlite"]

dep:rusqlite 意思是:只有开启 server 时,才装这个库。
前端打包时,它不会出现,避免“车开到浏览器里去”。


第二步:创建数据库 —— 打好地基

我们用 thread_local! 来管理数据库连接(技术细节先不管,你就当它是“安全保险箱”)。

#[cfg(feature = "server")]
thread_local! {
    pub static DB: rusqlite::Connection = {
        // 打开或创建 "hotdog.db" 文件
        let conn = rusqlite::Connection::open("hotdog.db").unwrap();

        // 创建一张叫 "dogs" 的表
        conn.execute_batch(r#"
            CREATE TABLE IF NOT EXISTS dogs (
                id INTEGER PRIMARY KEY,
                url TEXT NOT NULL
            );
        "#).unwrap();

        conn
    };
}

效果:运行后,你会看到项目里多了一个 hotdog.db 文件!它就是你的“数字狗窝”!


第三步:把狗图存进去 —— 从“写记事本”升级到“建档案”

原来的 save_dog 是往文本文件里写一行,
现在我们用 SQL 语句,把数据“规规矩矩”存进数据库:

#[server]
async fn save_dog(image: String) -> Result<(), ServerFnError> {
    DB.with(|db| {
        db.execute("INSERT INTO dogs (url) VALUES (?1)", &[&image])
    })?;
    Ok(())
}

解释:

  • INSERT INTO dogs:往 dogs 表插入数据
  • VALUES (?1):第一个参数是 image
  • ?1 是防SQL注入的安全写法

点一次“收藏”,就往表里加一条记录,整齐、安全、不重复!


测试一下:看看你的“狗窝”长啥样?

  1. 启动 App,点几下“收藏”
  2. 打开项目文件夹,找到 hotdog.db
  3. 用一个 SQLite 查看工具(比如 )打开它
  4. 查看 dogs

https://i-blog.csdnimg.cn/direct/68f09e7598b24c5bb560dd40abfd6da5.png

你应该能看到你收藏的所有狗图链接!
恭喜,你的 App 现在是个正经的“全栈应用”了!

Rust 里的数据库工具箱(推荐版)

Rust 的数据库生态正在飞速发展,这里是你该认识的“工具们”:

工具特点适合谁
rusqlite简单直接,无魔法,SQLite 专用小项目、初学者
Sqlx支持 PostgreSQL/MySQL/SQLite,异步友好中大型项目
SeaORM基于 Sqlx 的 ORM,像写 Rust 一样写数据库喜欢“面向对象”风格的人
Turbosql超简洁,自动推导,SQLite 专用追求极简的人
rust-postgresPostgreSQL 专用,API 类似 rusqlite只用 PG 的人

数据库放哪?——自己养 or 托管?

你可以:

  • 自己养:在自己电脑或服务器上跑数据库(免费,但要操心)
  • 托管服务:花钱让别人帮你管(省心,但要付钱)
  • 数据库三步走
步骤做什么类比
1. 选数据库单用户用 SQLite,多用户用 PostgreSQL住公寓还是住别墅?
2. 加依赖rusqlite + feature = "server"请个靠谱的装修队
3. 写代码创建表 → 插入数据 → 查看结果把狗从纸条搬到狗窝