前言
前面几篇我们基本都在 Web 视角里看 Dioxus:
rsx!怎么写- Signals 怎么驱动状态
- 组件怎么拆
- 路由怎么组织
但 Dioxus 真正有意思的地方,不只是"Rust 也能写页面"。
它最容易让人记住的一点其实是:同一套 Rust UI,浏览器里能跑,桌面窗口里也能跑。
问题也正出在这里。
很多人一看到"桌面端 + WebView",立刻就会联想到 Tauri,然后顺手给个结论:
哦,懂了,就是和 Tauri 一样,包个 WebView。
这话只说对一半。
因为两者的共同点确实很明显:UI 都是跑在系统 WebView 里的。但差别也很明显:Tauri 通常是前端一套、Rust 一套;Dioxus 则尽量把 UI、状态和组件这几件事留在同一个 Rust crate 里。
所以这篇不只讲"怎么执行一条命令把桌面窗口弹出来",更想讲清 4 件事:
- Dioxus Desktop 的运行链路到底是什么
- 它和 Tauri 明明都用 WebView,为什么写起来不像一回事
- 桌面能力怎么接进来,边界又在哪里
- 真要落项目,什么时候它合适,什么时候你该换路线
1. 先说结论:Dioxus Desktop 不是原生控件渲染,而是原生窗口 + 系统 WebView
先把最关键的一句摆前面:
Dioxus Desktop 不是"Rust 直接画原生按钮、原生输入框",而是"Rust 写组件树,最后把界面放进一个原生窗口里的 WebView"。
如果把它拆开看,大致是这 4 层:
- 你写的是 Rust 组件,入口通常还是
main.rs - 组件里的 UI 语法是
rsx! - 桌面端运行时会创建一个原生窗口
- 窗口内部嵌的是系统 WebView,真正显示出来的仍然是 HTML/CSS 渲染结果
也就是说,Dioxus Desktop 的"桌面",本质不是像 Qt、Makepad 那样自己画控件,而是:
- 外面是原生窗口
- 里面是浏览器渲染引擎
- 你写 UI 的语言不是 HTML/JS,而是 Rust
这个认知最好先立住,因为它会直接影响你后面对很多问题的预期:
- 为什么 CSS 依然很好使
- 为什么布局思路很像前端
- 为什么复杂动画和大列表的瓶颈,很多时候还是 WebView
- 为什么它和 Tauri 的性能边界有相似之处
2. dx serve 到底帮你做了什么
先看最直接的命令。
在前面的入门篇里,我们已经跑过 Web 版:
bash
dx serve
切到桌面端时,常见写法是:
bash
dx serve --platform desktop
如果你本机 CLI 显示的是:
bash
dx serve --desktop
那就以你当前 dx --help 为准。不同阶段的 CLI 参数写法可能会有变化,但核心动作没变:把当前项目切到桌面目标来启动。
很多人把这一步理解成"把 Web 项目装进一个桌面壳子里"。这么理解不算全错,但还是太粗。完整一点的运行链路,大概是这样:
2.1 第一步:dx 还是在调 Rust 编译链
dx 只是 Dioxus 官方给你准备的一层入口,底层绕不开的仍然是 Rust 工具链。也就是说:
- 项目依然是 Cargo 项目
- 依赖依然在
Cargo.toml - 代码改动后依然要经过 Rust 编译
这一点和很多纯前端打包器完全不同。
所以你在桌面端感觉"还挺顺",不代表它脱离了 Rust 编译,只是 Dioxus 把这条链路包得更顺手了。
2.2 第二步:桌面运行时创建原生窗口
编译通过后,Dioxus Desktop 会启动桌面运行时,创建一个真正的系统窗口。
这意味着你拿到的不是浏览器标签页,而是:
- Windows 上的独立窗口
- macOS 上的独立 App 窗口
- Linux 上的独立桌面窗口
这一层是"桌面感"的来源。
2.3 第三步:窗口里嵌入系统 WebView
窗口有了,但窗口本身不会画你的页面。
真正负责渲染的,是系统提供的 WebView 引擎。你可以把它理解成"应用内部自带的一块浏览器显示区",只不过这个浏览器不是你手动打开的 Chrome 标签页,而是桌面程序里嵌进去的渲染区域。
这一步也是 Dioxus Desktop 和 Tauri 的共同地基。
2.4 第四步:Dioxus 把组件更新同步到 WebView 里的 DOM
你写的 rsx!、信号状态、事件处理,最终还是要映射成浏览器能理解的那套东西。
所以当状态变化时,Dioxus 会重新计算相关组件,再把变更同步到 WebView 里的页面结构上。
你可以把它理解成:
- 上层心智是 Rust 组件
- 底层渲染目标仍然是浏览器 DOM
这也是为什么前几篇里学到的很多东西,切到桌面端几乎不用重学。
3. 为什么同一个项目几乎不用改,就能从 Web 切到 Desktop
这一点最好用代码看。
先放一个很普通的 Dioxus 页面:
rust
use dioxus::prelude::*;
fn main() {
dioxus::launch(App);
}
#[component]
fn App() -> Element {
let mut name = use_signal(|| String::from("Dioxus Desktop"));
let mut clicks = use_signal(|| 0);
rsx! {
div { class: "page",
h1 { "桌面端也能直接复用这套组件" }
p { "你好,{name}。" }
input {
value: "{name}",
oninput: move |evt| name.set(evt.value()),
placeholder: "改个标题试试"
}
button {
onclick: move |_| clicks += 1,
"点了 {clicks} 次"
}
}
}
}
这段代码有两个很关键的特点:
- 它没有写任何桌面专属 API
- 它也没有多一个前端项目目录
你跑 Web:
bash
dx serve
它就是浏览器里的页面。
你跑桌面:
bash
dx serve --platform desktop
它就是一个桌面窗口里的页面。
这里最关键的一点是:平台切换发生在运行目标层,而不是"我要不要换一套 UI 代码"这一层。
如果你是从 React + Tauri 的组合过来的,这种感觉会非常明显。
在 Tauri 里,最常见的组织方式通常是:
- 前端项目一套
- Rust 后端一套
- 中间靠 command / event / IPC 通信
而在 Dioxus 里,很多时候你写的只是:
- 一个 Rust 项目
- 一套组件
- 一套状态
- 根据平台切不同运行时
所以我一直觉得,它和 Tauri 虽然都站在 WebView 上,但写起来确实不是一类东西。
4. Dioxus Desktop 和 Tauri 的差别,不在"是不是 WebView",而在"代码边界画在哪"
先把结论写前面:
Tauri 解决的是"用 Rust 给现有前端应用加一个轻量桌面壳";Dioxus Desktop 更像是在解决"我想把整套 UI 和业务状态都留在 Rust 里"。
看一张对比表会更直观:
| 维度 | Dioxus Desktop | Tauri |
|---|---|---|
| 渲染载体 | 系统 WebView | 系统 WebView |
| UI 主要写法 | Rust 的 rsx! 组件 |
HTML / CSS / JS 或前端框架 |
| 项目组织 | 通常一套 Rust 项目 | 往往是前端项目 + Rust 项目 |
| 跨语言边界 | 更少 | 更明显 |
| 前端生态复用 | CSS 思路能复用,JS 生态复用少 | 前端生态几乎原样复用 |
| 适合团队 | Rust 主导 | 前端 + Rust 协作 |
4.1 Dioxus 的好处:单语言栈,切换更少
如果你是 Rust 开发者,Dioxus 很顺的地方在于:
- 组件是 Rust
- 状态是 Rust
- 事件处理是 Rust
- 路由还是 Rust
你不需要前一秒在 JSX 里改页面,后一秒再回 Rust 改 command,然后再处理两边的参数类型对齐。
说白了,它减少的是语言切换成本,不是 WebView 成本。
4.2 Tauri 的好处:前端生态来得更完整
但反过来说,Tauri 的优势也很明确。
如果你的团队本来就有成熟前端栈,比如:
- React
- Vue
- Svelte
- Tailwind CSS
- 一整套现成组件库
那 Tauri 会更直接。因为它不要求你把 UI 重写成 Rust 组件,前端同学几乎可以原封不动把现有能力带过来。
所以这两个工具不是谁"更先进",而是解决的问题不太一样:
- 想保留前端工程体系,看 Tauri
- 想把 UI 和业务状态尽量收进 Rust,看 Dioxus
5. 热更新体验为什么比想象中顺一点
很多人第一次切到 Dioxus Desktop,会有点意外:
Rust 写桌面 UI,为什么改个文案、样式、组件结构,反馈还挺快?
原因不复杂,因为你改的大量内容,本质上还是页面层的东西。
前面第 2 篇其实已经提过一遍,放到桌面端依然成立:
- 改
rsx!内容,反馈通常不错 - 改 CSS,反馈通常不错
- 改小块逻辑代码,也往往还行
- 改依赖、改
Cargo.toml、改大结构,还是得老老实实等编译
所以 Dioxus Desktop 的开发体感,某种意义上是两层东西叠在一起:
- 上面那层是前端式的页面修改反馈
- 下面那层仍然是 Rust 编译链
我对它的评价一直比较克制:它的反馈速度确实比"纯 cargo run 桌面 GUI"舒服很多,但也别把它想成脚本语言那种秒改秒生效。
预期摆正,体验会好很多。
6. 桌面专属能力怎么接进来
讲到这里,很多人会追问一句:
页面能跑进桌面窗口我懂了,那桌面程序该有的能力怎么办?
如果 Dioxus Desktop 只能把网页塞进窗口里,那它价值其实不大。真正让它像桌面程序的,是你可以继续往里接桌面能力。
6.1 文件对话框:这是最常见、也最实用的一类能力
举个例子,做一个桌面笔记应用、导入工具、图片处理器,十有八九都会遇到"让用户选文件"。
这类能力在桌面端很常见,配合 Rust crate 往往就能接进去。比如下面这个思路:
rust
use dioxus::prelude::*;
use rfd::FileDialog;
fn main() {
dioxus::launch(App);
}
#[component]
fn App() -> Element {
let mut picked = use_signal(|| String::from("还没选择文件"));
rsx! {
div {
h1 { "桌面文件选择示例" }
button {
onclick: move |_| {
if let Some(path) = FileDialog::new().pick_file() {
picked.set(path.display().to_string());
}
},
"选择文件"
}
p { "{picked}" }
}
}
}
这段代码想说明的不是 API 长什么样,而是一个很实际的事实:
你在 Dioxus Desktop 里接桌面能力,很多时候并不需要先穿过一个前端到 Rust 的 IPC 通道,因为你的 UI 本来就写在 Rust 里。
这点在实际开发里非常省心。
6.2 窗口控制、标题、尺寸、关闭行为
除了文件对话框,桌面程序还经常会碰到这些需求:
- 改窗口标题
- 控制初始宽高
- 最小化、最大化、隐藏
- 关闭窗口时改成托盘常驻而不是直接退出
这类能力通常会落在两类位置上:
- 启动阶段的桌面配置
- 运行中的窗口句柄或桌面上下文
你可以把它理解成:
- 静态配置:应用一启动就确定,比如默认窗口大小、标题、图标
- 动态控制:应用跑起来以后根据交互去改,比如点按钮最小化窗口
从这里开始,Dioxus 才真的有点桌面应用的味道。
6.3 系统菜单、托盘、原生集成
再往下走,就是更偏桌面生态的能力:
- 系统菜单
- 托盘
- 原生快捷键
- 平台相关行为
这些能力通常能做,但有个预期最好先摆正:Dioxus Desktop 的桌面原生能力覆盖,不太适合按"和 Tauri 完全同级"去理解。
Tauri 这些年在桌面集成这一层做得更深、更成熟;Dioxus 的核心优势仍然是"Rust UI 跨平台"这条线,而不是"原生壳能力卷到最全"。
所以如果你的项目高度依赖:
- 非常完整的系统菜单
- 很复杂的托盘交互
- 深度平台特性
- 大量成熟插件
那就要认真比较 Dioxus 和 Tauri,而不是只看"都能弹个窗"。
7. 打包怎么理解:dx bundle 解决的是"分发",不是"神奇跨编译"
开发阶段能跑起来,只能说明一半。
真正到项目往外发的时候,你迟早会碰到打包:
bash
dx bundle --platform desktop
它的目标很明确,就是把当前桌面应用整理成适合分发的桌面产物。
常见可以期待的结果,大致会是:
- macOS 的
.app/.dmg - Windows 的
.exe或安装器形态 - Linux 的
AppImage等发行物
但这里一定别想得太轻松。
7.1 打包不是"一条命令就跨三平台白嫖"
dx bundle 很好用,但它并不意味着你在一台机器上随手一敲,就能完美产出所有平台包。
桌面打包通常还会受这些现实条件影响:
- 当前宿主系统
- 目标平台工具链
- 图标和资源配置
- 代码签名
- 平台本身的打包规范
尤其是 macOS 和 Windows,这两边各有各的规矩,不是"Rust 能 cross compile"就一切自动搞定。
7.2 Dioxus.toml 更像桌面分发配置入口
到这一步,Dioxus.toml 的存在感就会明显增强。
它不只是个"顺手带着的配置文件",很多桌面应用关心的信息,都会往这里收:
- 应用名
- 图标
- bundle 相关参数
- 静态资源声明
所以如果你前面一直把它当成可有可无的文件,到桌面打包这里基本就该认真看它了。
8. 性能和体积,到底该怎么和 Tauri 比
这一段很容易在评论区吵起来,所以先把话说死一点:下面不是谁吊打谁,只是把 Dioxus Desktop 放回它实际的位置里。
8.1 先说性能:两边都逃不开 WebView 的天花板
Dioxus Desktop 和 Tauri 既然都基于系统 WebView,那就有一个很现实的共同点:
复杂页面的上限,很多时候不是由"你写的是 Rust 还是 JS"决定的,而是由 WebView 的渲染能力决定的。
比如这些场景:
- 超大表格
- 超长虚拟列表
- 高频动画
- 大量 DOM 更新
如果页面结构本身就很重,WebView 这一层的约束不会凭空消失。
所以别把"Rust 写 UI"自动等价成"桌面性能一定碾压前端栈"。这不是一回事。
8.2 再说体积:Dioxus 的重点不是比 Tauri 更小,而是更统一
很多人拿 Tauri 的卖点做第一反应:
Tauri 很轻,那 Dioxus 会不会也一样轻?
这得分开看。
两边都站在系统 WebView 上,所以它们都不像 Electron 那样自己带整套 Chromium,这一点先别混淆。
但 Dioxus 的重点并不是"把桌面包做到最小",而是:
- UI 留在 Rust
- 代码跨 Web / Desktop / Mobile
- 尽量减少多语言切换
所以如果你问我该怎么理解体积问题,我的答案很简单:别先比口号,先打出你自己的包。
因为影响桌面包大小的因素非常多:
- 依赖数量
- 是否启用额外功能
- 资源文件体积
- 构建 profile
- 平台差异
你真正该比较的不是抽象框架名,而是"同一个业务需求在两套方案里各自产生什么结果"。
8.3 它真正占便宜的地方,在研发路径
如果项目目标是:
- 一个内部工具
- 一个 MVP
- 一个管理后台 + 桌面客户端
- 一个希望以后还能顺手再上 Web 的产品
那 Dioxus Desktop 的价值很清楚:
它不一定在每个单点指标上都赢,但它能把整条研发路径收得更统一。
这往往比"某项 benchmark 更漂亮"更值钱。
9. 什么场景下我会推荐 Dioxus Desktop
说了这么多,最后还是要落到选型。
我会推荐 Dioxus Desktop 的场景,大概是这几类:
- 你本来就想用 Rust 做主要开发语言
- 你不想维护前端 + Rust 两套工程
- 你需要 Web 和桌面尽量共用一套 UI 与状态逻辑
- 你的产品更偏工具型、业务型,而不是极致原生 UI 动效型
反过来,如果是下面这些场景,我会更谨慎:
- 你已经有成熟前端团队和大量前端资产
- 你要深度吃平台原生壳能力
- 你非常在意复杂动画、渲染极限或原生控件质感
- 你希望直接复用整个 JS 组件生态
这个时候,Tauri、Makepad,甚至别的桌面路线,可能都更合适。
总结
如果这篇只留一句话,那就是:Dioxus Desktop 的重点,不是"Rust 也能套 WebView",而是"用 Rust 组件的写法,把 WebView 这条跨平台路线重新组织了一遍"。
它和 Tauri 的共同点,是都站在系统 WebView 上。
它和 Tauri 的真正差别,是:
- Tauri 更像"前端应用接上 Rust 桌面壳"
- Dioxus 更像"Rust UI 直接跨到桌面运行时"
所以它的卖点从来都不是"绝对最原生",也不是"绝对最轻"。真正吸引人的地方,是你可以顺着同一套组件和状态模型,把一套 Rust 代码继续铺到 Web、桌面、移动三端。
这也是为什么我觉得 Dioxus 值得单开一个系列来写。
这一篇主要回答的是:Dioxus Desktop 到底怎么跑起来,它和 Tauri 的差别到底落在哪。
下一篇如果继续往下接,一个很自然的话题就是:Dioxus Mobile 能做什么,和桌面这条线复用到什么程度,限制又在哪里。
如果你正在做 Rust 客户端选型,也欢迎把你的场景丢到评论区。工具型应用、内部系统、跨端 MVP,这几类问题最适合拿 Dioxus 来具体聊。