Rust Dioxus 桌面框架详细使用说明
Rust Dioxus 桌面框架详细使用说明
定位:Dioxus 是组件 + RSX + 信号的全 Rust 跨端 UI 框架——用 React 的心智(组件 / Props / Hooks),但状态用信号、更新靠订阅(细粒度、非虚拟 DOM 全树 diff)。一份代码目标 Web / 桌面 / 移动 / 全栈。
诚实前提:Dioxus 桌面今天 = 系统 WebView(wry/tao,与 Tauri 同底座),并非 iced/egui 那样的自绘;差异在「UI 全用 Rust RSX 写」而非 JS/HTML。自研的 Blitz/Dioxus Native(WGPU 直渲 HTML/CSS)是未来方向、仍属实验。
版本基线(2026-09):Dioxus 0.7.x(dioxus-desktop 0.7.x)。0.7 带来 Subsecond 热补丁(连 Rust 逻辑都能热替换)。框架年轻、迭代极快,版本间破坏性变更偏多。
一句话取舍:给「前端背景、想 all-in Rust、能接受追新」的团队;桌面渲染现状与 Tauri 无本质区别,选它多半是为了「全 Rust + 一份代码多端」。
图:8 张内嵌 base64 SVG。所有易变 API 处均标注「以 docs.rs 对应版本为准」。
姊妹篇:
docs/20260920_Rust-iced桌面框架使用说明.md(Elm 保留模式·自绘)、docs/20260920_Rust-egui桌面框架使用说明.md(立即模式·自绘)、docs/20260920_Rust桌面GUI框架横评.md(五框架横评)。
目录
- 一、Dioxus 是什么:组件 + RSX + 信号
- 二、安装与第一个程序
- 三、RSX:用 Rust 写 JSX
- 四、组件与 Props
- 五、信号 Signals:细粒度响应式
- 六、事件与受控输入
- 七、派生与共享:use_memo · use_effect · use_context
- 八、异步:use_resource
- 九、桌面 = 系统 WebView:与 Tauri 同底座
- 十、中文与 WebView:省心处与真正的坑
- 十一、样式与资源:CSS · asset! · 双主题
- 十二、一份代码多端:Web/桌面/移动/全栈
- 十三、完整实例:待办事项 Todo
- 十四、常见坑速查
- 十五、与 CMX 工作区的呼应
- 十六、版本与参考资源
一、Dioxus 是什么:组件 + RSX + 信号
Dioxus 只有三个核心概念,凑齐就懂大半:
- 组件 Component:一个返回
Element的普通函数(fn App() -> Element)。可带 Props、可嵌套组合成组件树——就是 React 组件的心智。 - RSX:声明 UI 的宏(
rsx! { … }),写法接近 JSX,但是编译期宏、有类型检查,不是字符串模板。 - 信号 Signal:响应式状态(
use_signal(|| 0))。读它就订阅它,改它就通知订阅者——只重新渲染读了这个信号的组件,天然细粒度。
与 React 的最大不同:React 靠虚拟 DOM 全树 diff 找变化;Dioxus 靠信号订阅精确定位——改一个信号,框架直接知道该更新哪几处,不用比对整棵树。心智像 React,机制更省。
与本系列另外两位的不同:iced 是 Elm 保留模式、egui 是立即模式,两者都自绘;Dioxus 是组件/信号,且桌面走 WebView(第 9 节详解)。三种世界观各有主场。
二、安装与第一个程序
Cargo.toml——桌面目标开 desktop feature:
[dependencies]
dioxus = { version = "0.7", features = ["desktop"] }
# 换目标只改 feature:web / mobile / fullstack
# 推荐装 CLI(热重载/多端构建):cargo binstall dioxus-cli → 命令是 dx
src/main.rs——最小计数器,全文如下:
use dioxus::prelude::*;
fn main() {
dioxus::launch(App); // 启动一个组件
}
// 组件 = 返回 Element 的函数(约定用大驼峰命名,rsx 才认它是组件)
fn App() -> Element {
// 信号:响应式状态。要改就加 mut
let mut count = use_signal(|| 0);
rsx! {
h1 { "计数: {count}" } // 读 count → 订阅;count 变这里自动更新
button { onclick: move |_| count += 1, "加一" }
button { onclick: move |_| count -= 1, "减一" }
}
}
dx serve 跑起来(带热重载),或 cargo run。对比 iced 的四件套、egui 的每帧 update,Dioxus 这里是组件 + 信号:状态是 use_signal,界面是 rsx!,点击直接 count += 1 改信号,读了它的地方自动刷新。
好消息:因为桌面是 WebView,中文默认就能显示(系统浏览器引擎提供字体),没有 iced/egui 那种「先装中文字体」的坑(第 10 节展开这个反转)。
三、RSX:用 Rust 写 JSX
rsx! 是声明 UI 的宏,长得像 JSX/HTML,但是编译期宏、带 Rust 类型检查。要素:
rsx! {
// 元素:名字 + 花括号;属性写 key: value,子节点直接嵌套
div {
class: "card",
id: "main",
// 文本 + 插值:{signal} / {表达式} 直接插进字符串
h1 { "计数: {count}" }
p { "两倍是 {count() * 2}" }
// 事件:on 开头,值是闭包
button {
onclick: move |_| count += 1,
"加一" // 子节点也可以是纯文本
}
// 循环:for 直接写在 rsx 里;列表项给稳定的 key
for todo in todos.read().iter() {
li { key: "{todo.id}", "{todo.text}" }
}
// 条件:if / else 也直接内联
if count() > 5 {
p { class: "warn", "有点多了!" }
}
}
}
| 语法 | 说明 |
|---|---|
div { … } | 元素;小写=HTML 元素,大写=组件(MyComp { … }) |
class: "x", onclick: … | 属性 / 事件,写成 key: value |
"文本 {signal}" | 文本子节点 + 插值({} 里可放信号或表达式) |
for x in it { … } | 列表渲染;每项建议给 key |
if cond { … } else { … } | 条件渲染 |
{ 表达式 } | 内联任意返回 Element 的 Rust 表达式 |
rsx!全程走 Rust 类型系统:属性名拼错、信号类型不对,编译期就报错——这是它比「字符串模板」强的地方。dx serve下改 rsx 还能热重载即时看到。
四、组件与 Props
组件就是返回 Element 的函数。要接收参数(Props),用 #[component] 宏把函数参数变成属性:
use dioxus::prelude::*;
// 用 #[component]:函数参数即 Props
#[component]
fn Greeting(name: String, count: i32) -> Element {
rsx! { p { "你好 {name},这是第 {count} 次" } }
}
// 父组件里像写 HTML 标签一样用它(大驼峰名)
fn App() -> Element {
rsx! {
Greeting { name: "张三".to_string(), count: 1 }
Greeting { name: "李四".to_string(), count: 2 }
}
}
// 需要更多控制(默认值/可选)时,手写 Props 结构体:
#[derive(Props, PartialEq, Clone)]
struct CardProps {
title: String,
#[props(default = false)] // 可选属性带默认值
highlighted: bool,
children: Element, // 接收子节点(类似 slot)
}
#[component]
fn Card(props: CardProps) -> Element {
rsx! {
div { class: if props.highlighted { "card hot" } else { "card" },
h3 { "{props.title}" }
{props.children} // 渲染传进来的子节点
}
}
}
要点:
- 组件名用大驼峰(
Greeting),rsx!靠大小写区分「组件」与「HTML 元素」。 children: Element让组件接收子节点(Card { Foo {} }里的Foo {}),类似插槽。- Props 需要
PartialEq:Dioxus 靠它判断属性变没变、要不要重渲子组件。
五、信号 Signals:细粒度响应式
信号是 Dioxus 状态管理的核心。三件事记牢:读=订阅、改=通知、Copy=随便传。
let mut count = use_signal(|| 0);
// —— 读 ——(在组件/rsx 里读 = 订阅它,之后它变你就重渲)
let now = count(); // 调用语法,最常用
let now = *count.read(); // 显式读
// —— 改 ——
count += 1; // 运算符
count.set(10); // 直接设
count.write().push(x); // 拿可变引用改内部(如 Vec/结构体)
*count.write() += 1; // 显式写
// —— Copy ——:信号是 Copy,可直接传给子组件、丢进闭包/async,无需 clone
let doubled = use_memo(move || count() * 2); // 派生信号
spawn(async move { count += 1; }); // 异步里也能用
细粒度响应式是它的精髓:
- 订阅发生在「读」处:某组件读了
count,count变时只有它(及其它读者)重跑;没读的组件纹丝不动。 - 因此没有虚拟 DOM 全树 diff——框架直接知道该更新谁。大列表、深层树里这很省。
- 只读场景给子组件传
ReadSignal<T>(只读信号),读写场景传Signal<T>。
对比:iced 每次
view重建整棵界面描述;egui 每帧重跑整个 UI;Dioxus 只重跑「读了变化信号」的组件——三者更新粒度递减,Dioxus 这点最接近 React 但更精确。
六、事件与受控输入
事件处理器是 on* 属性 + 闭包;受控输入把信号和输入框双向绑起来:
let mut name = use_signal(String::new);
rsx! {
input {
value: "{name}", // 信号 → 输入框(受控)
oninput: move |e| name.set(e.value()), // 输入框 → 信号
onkeydown: move |e| {
if e.key() == Key::Enter { /* 提交 */ }
},
}
p { "你好, {name}" } // name 一变,这里自动更新
// 常见事件:onclick / ondoubleclick / onmouseenter / onchange / onsubmit …
button {
onclick: move |e| {
e.stop_propagation(); // 事件对象有 DOM 那套方法
name.set(String::new());
},
"清空"
}
}
| 事件 | 取值 |
|---|---|
oninput / onchange | e.value() 拿输入内容 |
onclick / onmouse* | e.stop_propagation() / 坐标等 |
onkeydown / onkeyup | e.key()(Key::Enter 等) |
onsubmit | 表单提交(配 prevent_default) |
因为渲染层是 WebView,事件对象就是浏览器那套语义(
value()/key()/stop_propagation()…),前端经验可直接迁移。
七、派生与共享:use_memo · use_effect · use_context
除了 use_signal,常用 Hook 还有派生、副作用、跨组件共享:
// use_memo:派生值,依赖(这里是 count)变了才重算,结果本身也是信号
let doubled = use_memo(move || count() * 2);
// use_effect:副作用,依赖自动追踪;读了谁、谁变就重跑
use_effect(move || {
println!("count 变成了 {}", count()); // 日志/订阅外部/同步 DOM 等
});
// use_context_provider / use_context:跨层级共享状态,免 prop drilling
#[derive(Clone, Copy)]
struct Theme(Signal<bool>); // 建议用 newtype 包一层(按类型取)
fn App() -> Element {
use_context_provider(|| Theme(Signal::new(false))); // 父:提供
rsx! { Child {} }
}
fn Child() -> Element {
let theme = use_context::<Theme>(); // 子:任意深度取用
rsx! { p { "暗色模式: {theme.0}" } }
}
| Hook | 用途 |
|---|---|
use_signal | 响应式状态(最常用) |
use_memo | 派生值(依赖变才重算) |
use_effect | 副作用(依赖自动追踪) |
use_resource | 异步数据(第 8 节) |
use_context_provider / use_context | 跨组件共享,免逐层传参 |
use_future / spawn | 起一个后台异步任务 |
Context 按类型(TypeId)索引,所以要存多个同底类型(如多个
String),各用一个 newtype 包起来区分。
八、异步:use_resource
异步拉数据用 use_resource:组件里发起,任务后台跑,结果一到自动重渲——读它就订阅它。
fn UserCard() -> Element {
// 发起异步任务;返回一个 Resource,读它会订阅
let mut user = use_resource(move || async move {
reqwest::get("https://api.example.com/user")
.await?
.text()
.await
});
rsx! {
// 读 resource:None=还在跑,Some(Ok)=成功,Some(Err)=失败
match &*user.read() {
Some(Ok(text)) => rsx! { p { "用户:{text}" } },
Some(Err(e)) => rsx! { p { class: "err", "出错:{e}" } },
None => rsx! { p { "加载中…" } },
}
button { onclick: move |_| user.restart(), "刷新" } // 手动重跑
}
}
要点:
use_resource(|| async { … })首帧立即返回(值为None),任务在后台异步跑,完成后自动触发重渲。- 读到的是
Option<Result<T, E>>:None加载中、Some(Ok)成功、Some(Err)失败。 - 依赖自动追踪:闭包里读了别的信号,那个信号变时 resource 会自动重跑;也可手动
.restart()。 - 全栈项目里,把这里的
reqwest::get换成#[server]服务端函数即可前后端同源(第 12 节)。
match内联在 rsx 里的写法随版本略有差异,个别版本更推荐if let或把分支抽成函数——以你锁定版本的 docs.rs / 指南为准。
九、桌面 = 系统 WebView:与 Tauri 同底座
这是选型前必须看清的一节:Dioxus 桌面今天的渲染 = 系统 WebView,与 Tauri 同底座(wry/tao)。
- 你的 RSX/组件在 Rust 侧跑,维护一棵 VirtualDom,信号驱动精确 diff;变更喂给
dioxus-desktop→wry/tao→ 系统 WebView(Windows 的 WebView2 / macOS 的 WKWebView / Linux 的 WebKitGTK)渲染。 - 与 Tauri 的真正区别不在渲染,而在「UI 用什么写」:Dioxus 用 Rust RSX,Tauri 用任意 Web 前端(React/Vue/Svelte,或 Leptos/Yew)。二者甚至可组合——Dioxus 可以当 Tauri 的前端。
- 自绘是未来、非现在:Dioxus 押注的 Blitz/Dioxus Native(用 WGPU 直渲 HTML/CSS、Taffy 布局)成熟后才能摆脱三平台 WebView 差异,但目前仍是实验品。
- Web 目标则没有 WebView 中间层:同一份组件编到 WASM 直接跑浏览器。
一句诚实话:如果你冲着「像 iced/egui 那样的纯自绘、像素级跨平台一致」来,Dioxus 桌面今天给不了——它给的是「全 Rust 写 UI + 一份代码多端 + WebView 渲染」。想清楚要的是哪一个。
十、中文与 WebView:省心处与真正的坑
iced/egui 那节讲「怎么装中文字体」,到 Dioxus 这里反转了——因为渲染是 WebView:
省心的地方(WebView 白送):
- 中文默认就显示:系统浏览器引擎自带完整字体栈,
"中文"直接正常,无需set_fonts/ 打包字体。 - 输入法(IME)成熟:候选框、预编辑全由系统 WebView 处理,中文输入体验最省心。
- 复杂排版最强:换行、竖排、RTL、
emoji——浏览器级排版能力全都有。 - CSS 生态全量复用:Flexbox/Grid、动画、任意 CSS 框架照单全收。
真正的坑(转移到 WebView 跨平台差异):
| 坑 | 说明 |
|---|---|
| 三平台内核不同 | WebView2 / WKWebView / WebKitGTK,CSS/JS 行为有差异 |
| Linux 是重灾区 | WebKitGTK 兼容性/依赖是常见坑位,要留测试预算 |
| 包体依赖系统 WebView | 包很小(不带引擎),但 Windows 老系统需分发 WebView2 Runtime |
| 性能上限受 WebView 制约 | 虽无 JS 桥(UI 逻辑是 Rust),渲染仍是 WebView 天花板 |
一句话:Dioxus 把 iced/egui 的「字体坑」换成了「WebView 跨平台一致性坑」。中文/IME/排版省心了,但三平台 WebView 内核差异要专门测——这和 Tauri 是同一类烦恼。
十一、样式与资源:CSS · asset! · 双主题
Dioxus 的样式就是 CSS(因为渲染是 WebView)。静态资源用 asset! 登记、Stylesheet 注入:
use dioxus::prelude::*;
// asset!:编译期登记资源,产出带哈希的路径(自动进产物)
static MAIN_CSS: Asset = asset!("/assets/main.css");
fn App() -> Element {
rsx! {
document::Stylesheet { href: MAIN_CSS } // 注入全局样式表
// 主题:完全走 CSS 那套
div {
class: "card", // 类名切换
style: "padding: 16px", // 内联样式
"内容"
}
}
}
主题切换有多种 Web 惯用法,任选:
- class 切换:
div { class: "{theme}" },配一套.dark { … }CSS。 - CSS 变量:
color: var(--accent),切主题只改根变量。 - 媒体查询:
@media (prefers-color-scheme: dark)跟随系统。 - 也能直接上 Tailwind / 任意 CSS 框架。
对齐 CMX 硬约束 #4(双主题通路、禁硬编码色值):因为桌面就是 WebView,CMX 现有的
--sap*变量、data-cmx-skin/data-cmx-skin-tone切肤那一整套可原样复用(和给 Tauri 做前端时一模一样)——这也是 Dioxus 相对 iced/egui 在「双主题合规」上的天然便利:不用像自绘框架那样从 palette 手动派生,直接用你已有的 CSS 主题体系。
十二、一份代码多端:Web/桌面/移动/全栈
Dioxus 的招牌是一份组件代码、多端目标——切目标基本只改 dx serve --platform:
dx serve # 默认平台(按 Cargo.toml feature)
dx serve --platform desktop # 桌面(WebView)
dx serve --platform web # 网页(WASM)
dx serve --platform android # 安卓模拟器/真机
dx build --release --platform desktop # 出包
| 目标 | 渲染 / 形态 | 成熟度 |
|---|---|---|
| Web | 编到 WASM,浏览器直接跑 | 一级 |
| 桌面 | 系统 WebView(wry/tao) | 官方支持(本文重点) |
| 移动 | iOS / Android | 官方支持,生态较年轻 |
| 全栈 | #[server] 服务端函数(Axum 底座) | 一级 |
全栈的 #[server] 让前后端写在同一个 crate、同源调用:
// 这个函数只在服务端执行;客户端「像调普通 async 函数」一样调用它
#[server]
async fn save_todo(text: String) -> Result<(), ServerFnError> {
// 只有服务端能碰 DB / 密钥
db::insert(&text).await?;
Ok(())
}
// 组件里(客户端)直接 await 它,框架负责生成 RPC
fn AddButton(text: String) -> Element {
rsx! {
button {
onclick: move |_| {
let text = text.clone();
async move { let _ = save_todo(text).await; }
},
"保存到服务器"
}
}
}
这套 server functions 与 CMX 后端天然同族——Dioxus 全栈的底座就是 Axum,和 CMX 各引擎(axum)是一门技术。这也是它值得放进 CMX 观察名单的主要理由(第 15 节)。
十三、完整实例:待办事项 Todo
把前面的概念串起来——一个可编译的待办事项应用:
use dioxus::prelude::*;
fn main() {
dioxus::launch(App);
}
#[derive(Clone, PartialEq)]
struct Todo {
id: u64,
text: String,
done: bool,
}
// 信号是 Copy,可原样传进普通函数(这样两个事件处理器能复用同一段逻辑)
fn add_todo(mut input: Signal<String>, mut items: Signal<Vec<Todo>>, mut next_id: Signal<u64>) {
let text = input().trim().to_string();
if !text.is_empty() {
items.write().push(Todo { id: next_id(), text, done: false });
next_id.set(next_id() + 1);
input.set(String::new());
}
}
fn App() -> Element {
let input = use_signal(String::new);
let items = use_signal(Vec::<Todo>::new);
let next_id = use_signal(|| 0u64);
rsx! {
h1 { "待办事项" }
div {
input {
value: "{input}",
oninput: move |e| input.clone().set(e.value()),
onkeydown: move |e| if e.key() == Key::Enter { add_todo(input, items, next_id); },
}
button { onclick: move |_| add_todo(input, items, next_id), "添加" }
}
ul {
for item in items.read().iter() {
li { key: "{item.id}",
input {
r#type: "checkbox",
checked: item.done,
// item.id 是 Copy,被闭包按值捕获;items 是 Copy 信号
onchange: move |_| {
let mut items = items;
if let Some(t) = items.write().iter_mut().find(|t| t.id == item.id) {
t.done = !t.done;
}
},
}
span { "{item.text}" }
button {
onclick: move |_| { let mut items = items; items.write().retain(|t| t.id != item.id); },
"删除"
}
}
}
}
p { "共 {items().len()} 项 · 已完成 {items().iter().filter(|t| t.done).count()}" }
}
}
这段覆盖了:信号状态、oninput 受控输入、回车提交、for 列表 + key、闭包按值捕获 Copy 信号、items.write() 改集合——就是图 8 那个界面。想接后端?把新增/删除换成 #[server] 函数即可(第 12 节)。中文无需额外处理(WebView 自带,第 10 节)。
十四、常见坑速查
| 坑 | 症状 | 正解 |
|---|---|---|
| 组件名用小写 | rsx 把它当 HTML 元素,渲染不出来 | 组件一律大驼峰(MyComp) |
| 改了信号界面不动 | 那个组件根本没「读」它 | 订阅在读处;确保渲染里读了该信号 |
列表少写 key | 增删时错位/状态串味 | for 项给稳定 key: "{id}" |
闭包里 clone 报错/繁琐 | 以为信号要 clone | 信号是 Copy,直接传/捕获即可 |
Props 没 PartialEq | 编译报错或无法判断重渲 | Props #[derive(Props, PartialEq, Clone)] |
| Linux 跑不起来/白屏 | 缺 WebKitGTK 依赖 | 装系统 WebKitGTK 开发库;留跨平台测试 |
| 以为是自绘、追求像素一致 | 三平台观感有差异 | 桌面=WebView,与 Tauri 同类;差异要测 |
| 照抄旧教程编译不过 | 0.4/0.5 API 差异大 | 认准 0.7 的 docs.rs / 指南,别跨版本抄 |
dx 命令找不到 | 没装 CLI | cargo binstall dioxus-cli(命令是 dx) |
十五、与 CMX 工作区的呼应
对照本工作区(元数据驱动企业平台,前端以 Web 资产为主):
- Dioxus 值得放进观察名单,但当下非首选。横评(
docs/20260920_Rust桌面GUI框架横评.md)结论:面向最终用户的桌面产品首选 Tauri 2(直接复用frontend/的 UI5/Tabler 与双主题资产);Dioxus 桌面今天 = WebView,与 Tauri 相比无渲染优势,选它主要为「UI 也全用 Rust 写」。 - 它的独特价值 = 全栈同族:
#[server]服务端函数的底座是 Axum,与 CMX 各引擎(axum)是一门技术。若未来希望「门户小工具类页面」也统一成 Rust、且前后端同源,Dioxus 全栈是顺理成章的路径。 - 双主题零成本对齐:因为是 WebView,CMX 的
--sap*变量 +data-cmx-skin切肤体系可原样复用,天然满足硬约束 #4(第 11 节)——这点与给 Tauri 做前端完全一致。 - 中文/IME 省心:WebView 自带,无自绘框架的字体坑(第 10 节)。
- 组合而非二选一:Dioxus 甚至可作为 Tauri 的前端(全 Rust RSX + Tauri 的插件/打包/安全模型),这是很多团队的实际用法。
与本系列的分工:iced=架构可演进的自绘桌面产品;egui=最快出活的内部工具/诊断面板;Dioxus=前端背景、想全 Rust 一份代码多端(桌面走 WebView)。三者主场不同,Dioxus 在 CMX 语境下更多是「未来若要 Rust 全栈前端」的候选。
十六、版本与参考资源
版本基线(2026-09):Dioxus 0.7.x。亮点是 Subsecond 热补丁(连 Rust 逻辑都能热替换,不只前端资源)。Dioxus 迭代极快、破坏性变更也最多(0.4→0.5→0.6→0.7 每次 API 都有明显变化)——把「升级成本」计入选型,认准你锁定版本的文档。
| 资源 | 地址 | 说明 |
|---|---|---|
| 官网 & 指南 | dioxuslabs.com(learn/0.7) | 按版本号的 Guide,教程成体系 |
| API 文档 | docs.rs/dioxus | 锁定你的版本看,一切以此为准 |
| 源码 & 示例 | github.com/DioxusLabs/dioxus(examples/) | todos / router / fullstack 等可跑范例 |
| CLI | dx(cargo binstall dioxus-cli) | dx new 脚手架、dx serve 热重载 |
| 迁移 | dioxuslabs.com/learn/0.7/migration | 升级到 0.7 的破坏性变更清单 |
学习路径建议:
dx new生成模板,跑官方examples/里的todos与fullstack(正好覆盖本文的信号 + server functions),再回头对照各节。遇到 API 对不上,第一反应是「看 0.7 的 docs.rs / Guide」——Dioxus 的锅九成是版本漂移。
一句话收束
Dioxus = React 心智、Rust 身体、一份代码多端:组件 + RSX + 信号,读即订阅、改即精确更新;桌面今天走系统 WebView(与 Tauri 同底座,中文/IME 省心、但要吃三平台差异),自绘的 Blitz 是未来。给「前端背景、想 all-in Rust、愿意追新」的团队;在 CMX 语境里,它更是「未来若要 Rust 全栈前端」的候选,而非当下替代 Tauri 的理由。
参考:dioxuslabs.com(learn/0.7、blog)、docs.rs/dioxus 与 docs.rs/dioxus-desktop、github.com/DioxusLabs/dioxus(examples)。版本以 2026-09 的 0.7.x 线为准;凡涉及具体 API,请以你锁定版本的 docs.rs / Guide 为准,勿跨版本照抄。
更多推荐


所有评论(0)