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 暗地里做了三件事:
- 在前端:变成一个
reqwest
请求,发给后端 - 在后端:变成一个
axum
接口,接收请求 - 自动注册:启动时自动挂载到路由上,比如
/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
只在后端编译,前端完全看不到!
第三步:管理依赖——谁该上车,谁该下车?
有些库只能在服务器上跑,比如 tokio
、std::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; // 调用服务器函数!},"收藏!"
}
效果:
- 点“收藏”
- 当前狗图链接传给后端
- 后端写入
dogs.txt
- 页面自动换狗
从此,你的“心动小狗”都有地方安家了!
完整代码
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 | https://dog1.jpg |
2 | https://dog2.png |
3 | https://dog3.gif |
它不仅能存数据,还能:
- 快速查找
- 防止重复
- 自动编号
- 不怕断电
- 多人同时用也不打架
比记事本强一万倍!
选个啥数据库?——先看你是“独居”还是“合租”
市面上数据库五花八门,挑花眼?
别急,我们按场景分类:
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注入的安全写法
点一次“收藏”,就往表里加一条记录,整齐、安全、不重复!
测试一下:看看你的“狗窝”长啥样?
- 启动 App,点几下“收藏”
- 打开项目文件夹,找到
hotdog.db
- 用一个 SQLite 查看工具(比如 DB Browser for SQLite)打开它
- 查看
dogs
表
你应该能看到你收藏的所有狗图链接!
恭喜,你的 App 现在是个正经的“全栈应用”了!
Rust 里的数据库工具箱(推荐版)
Rust 的数据库生态正在飞速发展,这里是你该认识的“工具们”:
工具 | 特点 | 适合谁 |
---|---|---|
rusqlite | 简单直接,无魔法,SQLite 专用 | 小项目、初学者 |
Sqlx | 支持 PostgreSQL/MySQL/SQLite,异步友好 | 中大型项目 |
SeaORM | 基于 Sqlx 的 ORM,像写 Rust 一样写数据库 | 喜欢“面向对象”风格的人 |
Turbosql | 超简洁,自动推导,SQLite 专用 | 追求极简的人 |
rust-postgres | PostgreSQL 专用,API 类似 rusqlite | 只用 PG 的人 |
数据库放哪?——自己养 or 托管?
你可以:
- 自己养:在自己电脑或服务器上跑数据库(免费,但要操心)
- 托管服务:花钱让别人帮你管(省心,但要付钱)
- 数据库三步走
步骤 | 做什么 | 类比 |
---|---|---|
1. 选数据库 | 单用户用 SQLite,多用户用 PostgreSQL | 住公寓还是住别墅? |
2. 加依赖 | rusqlite + feature = "server" | 请个靠谱的装修队 |
3. 写代码 | 创建表 → 插入数据 → 查看结果 | 把狗从纸条搬到狗窝 |