十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Dioxus HotDog 示例实战:一个可跑在 Web、桌面与移动端的 Fullstack 狗狗图片查看器

Dioxus HotDog 示例实战:一个可跑在 Web、桌面与移动端的 Fullstack 狗狗图片查看器 Dioxus HotDog 示例实战一个可跑在 Web、桌面与移动端的 Fullstack 狗狗图片查看器【免费下载链接】dioxusFullstack app framework for web, desktop, and mobile.项目地址: https://gitcode.com/GitHub_Trending/di/dioxus本文以仓库中的examples/01-app-demos/hotdog目录为例讲解 Dioxus 官方教程演示应用 HotDog 的完整实现从dx serve --platform多端运行方式到前端组件、服务端函数server functions、内存 SQLite 存储再到基于 Dockerfile 与 Fly.io 的生产部署配置。读完后你可以掌握 Dioxus fullstack 模式下一份代码、前后端同仓的典型项目结构与运行、部署方式。一、HotDog 是什么以及如何运行HotDog 是 Dioxus 团队为新教程准备的演示应用Hot diggity dog!。它的功能很简单但麻雀虽小五脏俱全打开主页面通过外部狗狗图片 API 随机加载一只狗的头像点击 skip 换一只点击 save! 把当前图片保存为收藏收藏页面可以查看最近的收藏并支持删除。按照 README 的说明运行方式非常直接先进入该目录然后用dx命令为任意目标平台启动开发服务器# 先切换到示例目录 cd dioxus/hotdog # 任选其一 dx serve --platform web dx serve --platform desktop dx serve --platform ios dx serve --platform android一条dx serve命令即可在 Web、桌面、iOS、Android 四个平台上跑起来这正是 Dioxus 的多端目标同一份源码编译到不同渲染后端。二、项目结构与关键配置该示例的目录非常精简是学习 fullstack 项目的理想起点main.rs应用入口定义路由枚举并dioxus::launch(app)frontend.rs前端页面组件DogView、Favorites、NavBarbackend.rs服务端函数负责读写收藏数据assets/main.css全局样式Cargo.toml 与 Dioxus.toml依赖与平台特性、打包标识配置Dockerfile 与 fly.toml生产部署配置。Cargo.toml 中的依赖与特性划分体现了 Dioxus fullstack 的典型写法[dependencies] dioxus { workspace true, features [fullstack, router] } reqwest { workspace true, features [json] } serde { workspace true, features [derive] } serde_json { workspace true } rusqlite { version 0.32.0, optional true, features [bundled] } # Bundle SQLite so Windows/MSVC builds do not require an external sqlite3.lib. anyhow { workspace true } [features] default [web, server] web [dioxus/web] desktop [dioxus/desktop] native [dioxus/native] mobile [dioxus/mobile] server [dioxus/server, dep:rusqlite] production []几个值得注意的点fullstack特性让前端与后端运行在同一项目里后端接口以服务端函数的形式暴露给前端调用。server特性同时激活dioxus/server并引入rusqlite依赖。由于rusqlite是 optional 依赖非服务端构建如纯浏览器端不会编译 SQLite保持产物轻量注释也说明了使用bundled特性是为了让 Windows/MSVC 构建无需外部sqlite3.lib。production特性一个空特性开关仅用于标记生产构建入口代码会根据它决定服务器地址见后文。Dioxus.toml 则声明了应用名与打包标识[application] name hot_dog [bundle] identifier com.dioxuslabs publisher Dioxus Labsidentifier是移动端/桌面端打包时使用的 Bundle Identifier。三、应用入口路由与服务器地址main.rs 完整展示了 fullstack 应用的入口形态mod backend; mod frontend; use dioxus::prelude::*; use frontend::*; #[derive(Routable, PartialEq, Clone)] enum Route { #[layout(NavBar)] #[route(/)] DogView, #[route(/favorites)] Favorites, } fn main() { // only in production should we set the URL, otherwise let dx do the work #[cfg(all(not(feature server), feature production))] dioxus::fullstack::set_server_url(https://hot-dog.fly.dev); dioxus::launch(app); } fn app() - Element { rsx! { Stylesheet { href: asset!(/assets/main.css) } Router::Route {} } }Route枚举通过#[derive(Routable)]派生路由信息两个页面分别是DogView/与Favorites/favorites#[layout(NavBar)]指定NavBar作为布局组件包裹所有路由页面。app()里用Stylesheet挂载asset!(/assets/main.css)asset!是 Dioxus 的资产编译宏由构建系统在编译期把资产打包并生成解析代码因此无需在运行时手动拷贝静态文件。main()中有一段条件编译只有在非服务端not(feature server)且生产构建feature production时才调用set_server_url把后端地址固定为部署好的 Fly.io 地址开发模式下则由dx工具自动注入正确的本地服务器地址。set_server_url的实现在 client.rs它决定了前端发起服务端函数请求时的目标 URL。四、前端页面DogView与Favorites前端逻辑全部在 frontend.rs 中核心是use_loader这一响应式加载钩子——它在首次挂载或restart()被调用时执行一个异步闭包返回Result成功值可以直接当Signal读取失败则进入Suspense的错误状态。4.1DogView随机加载狗狗图片#[component] pub fn DogView() - Element { let mut img_src use_loader(|| async move { #[derive(Deserialize, Serialize, Debug, PartialEq)] struct DogApi { message: String, } let json reqwest::get(https://dog.ceo/api/breeds/image/random) .await? .json::DogApi() .await?; let url json.message; dioxus::Ok(url) })?; rsx! { div { id: dogview, img { id: dogimg, src: {img_src} } } div { id: buttons, button { id: skip, onclick: move |_| img_src.restart(), skip } button { id: save, onclick: move |_| async move { _ save_dog(img_src()).await }, save! } } } }加载器直接在前端用reqwest请求随机狗狗图片接口serde反序列化出图片 URLskip 按钮调用img_src.restart()让 loader 重新执行——这是use_loader提供的重新加载能力save! 按钮则跨端调用服务端函数save_dog在backend.rs中定义保存成功后数据留在服务端。NavBar组件同一文件则是路由的布局壳#[component] pub fn NavBar() - Element { rsx! { div { id: title, span {} Link { to: Route::DogView, h1 { HotDog! } } Link { to: Route::Favorites, id: heart, ♥️ } } Outlet::Route {} } }两个Link分别指向Route::DogView与Route::FavoritesOutlet::Route是 Dioxus 路由的占位出口当前匹配到的页面会渲染在这里。4.2Favorites列出与删除收藏#[component] pub fn Favorites() - Element { let mut favorites use_loader(list_dogs)?; rsx! { div { id: favorites, for (id , url) in favorites.cloned() { div { class: favorite-dog, key: {id}, img { src: {url} } button { onclick: move |_| async move { _ remove_dog(id).await; favorites.restart(); }, ❌ } } } } } }注意这里use_loader(list_dogs)直接传入了服务端函数本身前端把list_dogs当作普通异步闭包来 awaitDioxus fullstack 会在底层把它转换为一次 HTTP 请求发到服务端。删除时调用remove_dog(id)成功后再favorites.restart()刷新列表——乐观地调用、然后重新加载是这个示例采用的最简同步策略。五、服务端函数属性宏路由 线程本地 SQLitebackend.rs 是整个示例最有信息量的文件展示了 Dioxus 服务端函数的完整写法use anyhow::Result; use dioxus::prelude::*; #[cfg(feature server)] thread_local! { static DB: std::sync::LazyLockrusqlite::Connection std::sync::LazyLock::new(|| { let conn rusqlite::Connection::open(:memory:).expect(Failed to open database); conn.execute_batch( CREATE TABLE IF NOT EXISTS dogs ( id INTEGER PRIMARY KEY, url TEXT NOT NULL );, ) .unwrap(); conn }); } #[get(/api/dogs)] pub async fn list_dogs() - ResultVec(usize, String) { DB.with(|db| { Ok(db .prepare(SELECT id, url FROM dogs ORDER BY id DESC LIMIT 10)? .query_map([], |row| Ok((row.get(0)?, row.get(1)?)))? .collect::ResultVec(usize, String), rusqlite::Error()?) }) } #[delete(/api/dogs/{id})] pub async fn remove_dog(id: usize) - Result() { DB.with(|db| db.execute(DELETE FROM dogs WHERE id ?1, [id]))?; Ok(()) } #[post(/api/dogs)] pub async fn save_dog(image: String) - Result() { DB.with(|db| db.execute(INSERT INTO dogs (url) VALUES (?1), [image]))?; Ok(()) }实现要点#[get]/#[post]/#[delete]属性宏把普通 async 函数声明为服务端函数路径即 REST 风格接口GET /api/dogs、POST /api/dogs、DELETE /api/dogs/{id}。前端直接以函数引用list_dogs、save_dog、remove_dog调用它们无需手写 HTTP 客户端remove_dog(id: usize)的参数从路径{id}中自动解析。#[cfg(feature server)]门控thread_local!数据库连接以及全部服务端逻辑只在开启server特性的构建中存在。结合 Cargo.toml 中server [dioxus/server, dep:rusqlite]可以确认纯客户端构建不会链接任何数据库代码。存储选型使用rusqlite打开:memory:内存数据库并用thread_localLazyLock让每个线程持有独立的ConnectionSQLite 连接不是Sync的线程本地是 rusqlite 的典型用法表结构就一张dogs(id INTEGER PRIMARY KEY, url TEXT)。list_dogs按id DESC LIMIT 10只返回最近 10 条收藏。从源码结构看这个示例刻意选择内存数据库是为了教学上的零配置——数据随进程消失。若要持久化只需把:memory:换成文件路径仓库中附带的 fly.toml 也确实声明了一个挂载卷source hotdogdb挂载到/usr/local/app/hotdogdb说明部署方保留了挂持久卷的位置。六、样式单文件 CSS 的简单布局assets/main.css 用不到 150 行 CSS 完成了全部视觉深色背景#0e0e0e、Flex 布局的#dogview居中展示图片、#buttons横排两个大按钮#skip灰色、#save绿色、收藏页#favorites-container的换行流式布局以及一个小巧的交互细节——收藏图片上的删除按钮默认display: none只有.favorite-dog:hover button才显示即悬停才露出删除按钮。七、生产构建与部署Dockerfile Fly.io该示例同时给出了完整的生产部署链路。Dockerfile 采用多阶段构建FROM rust:1 AS chef RUN cargo install cargo-chef WORKDIR /app FROM chef AS planner COPY . . RUN cargo chef prepare --recipe-path recipe.json FROM chef AS builder COPY --fromplanner /app/recipe.json recipe.json RUN cargo chef cook --release --recipe-path recipe.json COPY . . RUN curl -L --proto https --tlsv1.2 -sSf dx 安装脚本 | bash RUN dx bundle --platform web --features production FROM chef AS runtime COPY --frombuilder /app/target/dx/hotdog/release/web/ /usr/local/app ENV PORT8080 ENV IP0.0.0.0 EXPOSE 8080 WORKDIR /usr/local/app ENTRYPOINT [ /usr/local/app/server ]上面dx 安装脚本处原文是从 DioxusLabs 官方仓库拉取的install.sh安装脚本此处为避免外部链接做脱敏表述。关键步骤解读cargo-chef 分阶段planner阶段先生成依赖清单builder阶段先编译依赖再拷贝源码充分利用 Docker 层缓存加速 Rust 构建dx bundle --platform web --features production这是生产构建的核心命令。production特性触发main.rs中的条件编译把服务端地址固定为已部署的https://hot-dog.fly.dev产物落在target/dx/hotdog/release/web/运行阶段直接把整个 web 产物目录拷进镜像ENTRYPOINT指向其中的server可执行文件——即 Dioxus fullstack 的 axum 服务端进程它会同时托管前端静态资源与/api/dogs服务端函数接口监听0.0.0.0:8080。fly.toml 是配套的 Fly.io 部署描述app hot-dog primary_region sjc [http_service] internal_port 8080 force_https true auto_stop_machines stop auto_start_machines true min_machines_running 0 processes [app] [[vm]] memory 1gb cpu_kind shared cpus 1 [mounts] source hotdogdb destination /usr/local/app/hotdogdbinternal_port 8080与 Dockerfile 的ENV PORT8080对应min_machines_running 0 自动启停意味着闲置时可零实例运行mounts声明的hotdogdb卷则与 SQLite 数据库的落盘位置预留对应。这也解释了main.rs里set_server_url(https://hot-dog.fly.dev)这一地址的由来。八、小结从示例能学到的 fullstack 模式HotDog 示例用最小代码量串起了 Dioxus fullstack 的几个核心机制机制在示例中的体现参考文件多端运行dx serve --platform web/desktop/ios/androidREADME特性驱动的前后端分离编译server/production特性门控Cargo.toml路由与布局Routable派生、Link、Outletmain.rs、frontend.rs响应式异步加载use_loader、restart()frontend.rs服务端函数#[get]/#[post]/#[delete]属性宏backend.rs资产编译asset!(/assets/main.css)Stylesheetmain.rs、main.css生产部署dx bundle 多阶段 Docker Fly.ioDockerfile、fly.toml如果你想在自己项目里复刻这套结构最小步骤是开启dioxus的fullstack特性用#[get]/#[post]等属性宏定义服务端函数前端用use_loader直接 await 这些函数开发时dx serve --platform web即可联调生产时用dx bundle --platform web --features production生成可独立部署的server进程。【免费下载链接】dioxusFullstack app framework for web, desktop, and mobile.项目地址: https://gitcode.com/GitHub_Trending/di/dioxus创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表