源码地图
主线位置:不在主线上。本页说明三条主线经过的代码分在哪些 package、哪些文件,package 之间怎么依赖,门面 crate
dioxus怎么把它们按 feature 拼起来。
dioxus 仓库在 tag v0.7.10 的根 Cargo.toml 是一个工作区,cargo metadata --no-deps 列出 112 个成员:
| 类别 | 个数 | 位置 |
|---|---|---|
| 库和工具 | 57 | packages/ 下 56 个,加上根目录的 dioxus-examples |
| CLI 测试用的小工程 | 19 | packages/cli-harnesses/* |
| Playwright 端到端测试工程 | 18 | packages/playwright-tests/* |
| 完整示例 | 15 | examples/01-app-demos、07-fullstack、10-integrations |
| subsecond 的测试工程 | 3 | packages/subsecond/subsecond-tests/* |
[workspace.package] 的 version = "0.7.10",绝大多数 package 用 version.workspace = true,所以一起发版。例外是 const-serialize 和 const-serialize-macro(0.8.0-alpha.0)、manganis-core-07(0.7.2)。下文只讲那 57 个库和工具,按层分成七组。
本卷涉及的 package 之间是这样依赖的(只画 [dependencies] 里的 dioxus 系 crate,省略 core-types、cli-config、devtools 这类小工具 crate):
flowchart TB facade["dioxus 门面"] --> web["dioxus-web"] facade --> server["dioxus-server"] facade --> fs["dioxus-fullstack"] facade --> router["dioxus-router"] facade --> hooks["dioxus-hooks"] server --> ssr["dioxus-ssr"] server --> fscore["dioxus-fullstack-core"] server --> router fs --> fscore fs --> fsmacro["dioxus-fullstack-macro"] web --> interp["dioxus-interpreter-js"] web -.->|"hydrate"| fscore web --> html["dioxus-html"] router --> html html --> hooks hooks --> signals["dioxus-signals"] fscore --> signals signals --> core["dioxus-core"] ssr --> core interp --> core core --> gbox["generational-box"] signals --> gbox html --> cmacro["dioxus-core-macro"] cmacro --> rsx["dioxus-rsx"]
几条值得记住的依赖关系:
dioxus-core不依赖任何渲染器、也不依赖 signals。 它的 dioxus 系依赖只有dioxus-core-types、generational-box,外加热补丁用的subsecond。ReactiveContext在 core 里,Signal在上一层的dioxus-signals里:core 只知道「有个上下文被标脏了」,不知道是谁标的。dioxus-ssr只依赖dioxus-core和dioxus-core-types。 服务端渲染 HTML 不需要dioxus-html:元素名、属性名都已经在Template里,是字符串。dioxus-rsx不依赖 dioxus 的任何 crate。 它是rsx!的解析和代码生成库(syn/quote),dioxus-core-macro是包装它的过程宏 crate;dioxus-autofmt(dx fmt)和dioxus-rsx-hotreload也复用它。dioxus-server依赖dioxus-router。 依赖挂在它默认打开的routerfeature 上(带 router 的streamingfeature),ssr.rs用ParseRouteError认出「路由不匹配」,把响应状态码改成 404。dioxus-web只在hydratefeature 下依赖dioxus-fullstack-core(虚线),而且依赖时写死了features = ["web"];dioxus-server依赖它时写死了features = ["server"]。所以fullstack-core里的#[cfg(feature = "web")]和#[cfg(feature = "server")]是由「哪个渲染器把它拉进来」决定的,见怎么读这一卷。
仓库里有一份 notes/architecture/00-OVERVIEW.md(2025-12-29 加入,开头说是写给「future Claude agents (and human contributors)」的),里面有一棵 crate 树。那棵树是按主题分的,不是依赖图:它把 dioxus-autofmt 画在 dioxus-rsx 下面,实际是 autofmt 依赖 rsx;它列的 dioxus-cli-opt 在这个 tag 的 packages/ 里不存在。本页的依赖以各 Cargo.toml 为准。
行数是 find <dir>/src -name '*.rs' -print0 | xargs -0 cat | wc -l 在 tag 解包上的结果,不含 tests/、benches/ 和示例。
核心:组件、模板与 VirtualDom
| package | src/ 行数 | 管什么 | 入口和主要文件(行数) | 讲它的部分 |
|---|---|---|---|---|
dioxus-core | 10 678 | VirtualDom、Runtime、scope、调度、diff、Mutations、事件、suspense 与错误边界 | virtual_dom.rs 808、nodes.rs 1 152(VNode、Template、DynamicNode)、diff/ 1 767、runtime.rs 654、events.rs 677、suspense/ 841、mutations.rs 423、scope_context.rs 417、reactive_context.rs 413、tasks.rs 405、scheduler.rs 289、scope_arena.rs 142 | 03 VirtualDom 内核、Template 与 VNode |
dioxus-rsx | 5 515 | rsx! 的解析和代码生成(不是过程宏 crate) | rsx_call.rs(CallBody)、template_body.rs 420(TemplateBody,生成 Template)、attribute.rs 917、element.rs 736、component.rs 534、ifmt.rs 419("{x}" 格式化字符串) | rsx! 展开成什么 |
dioxus-core-macro | 2 665 | 过程宏:rsx!、#[component]、#[derive(Props)] | lib.rs 42(rsx 转给 dioxus-rsx)、props/mod.rs 1 845、component.rs 664 | #[component] 与 Props |
dioxus-core-types | 170 | core 和渲染器共用的小类型:事件是否冒泡(bubbles.rs)、格式化 | bubbles.rs 105 | 事件 |
dioxus-html | 10 962 | HTML / SVG 元素和属性的定义、事件数据类型(MouseData、FormData……),大部分是宏生成的表 | attribute_groups.rs 2 516、elements.rs 2 168、events/ | 事件委托 |
dioxus-core 的 src/lib.rs 把二十几个模块都声明成私有(mod virtual_dom;),再 pub use 出需要的类型;另有一个 #[doc(hidden)] pub mod internal,放的是只给 rsx! 展开后的代码用的热重载类型。所以文档里看到的 dioxus_core::VirtualDom、dioxus_core::Element 看不出在哪个文件;Element 就定义在 lib.rs 里,是 Result<VNode, RenderError>。
响应式:signal、hooks 与 store
| package | src/ 行数 | 管什么 | 主要文件(行数) | 讲它的部分 |
|---|---|---|---|---|
generational-box | 1 847 | 让句柄能 Copy 的存储:Owner 持有值,GenerationalBox 是「指针 + 代数」 | lib.rs 450(GenerationalBox、Owner)、unsync.rs 392、sync.rs 396、error.rs 166 | generational-box |
dioxus-signals | 4 287 | Signal、Memo、ReadSignal、GlobalSignal,读写 trait | signal.rs 550(Signal)、read.rs 534、write.rs 766、memo.rs 274、global/ 373、copy_value.rs 210 | Signal、Memo 与 effect、context 与全局 signal |
dioxus-hooks | 2 104 | use_signal、use_memo、use_effect、use_resource、use_future、use_coroutine、use_context、use_action | use_resource.rs 559、use_action.rs 336、use_future.rs 198、use_reactive.rs 143、use_coroutine.rs 141 | 04 响应式 |
dioxus-stores | 2 945 | 0.7 新增的 Store:按字段、按元素订阅 | subscriptions.rs 501、store.rs 449、impls/ 1 749(Vec、HashMap、Option、Result……) | Store |
dioxus-stores-macro | 1 077 | #[derive(Store)]、#[store] | derive.rs 616 | Store |
use_signal 本身只有几行(use_signal 在 hooks/src/use_signal.rs 里),Signal 的机制全在 dioxus-signals 和 generational-box 里。
渲染器:web、SSR 和其他
| package | src/ 行数 | 管什么 | 主要文件(行数) | 讲它的部分 |
|---|---|---|---|---|
dioxus-web | 4 247 | 浏览器里的 wasm 渲染器:主循环、WebsysDom、事件转换、水合、history、document | lib.rs 266(run 主循环)、dom.rs 186、mutations.rs 262、events/ 1 634、hydration/ 418、history.rs 408、document.rs 303、devtools.rs 358 | 05 浏览器渲染、水合 |
dioxus-interpreter-js | 464 行 Rust + 1 693 行 TypeScript | 在 JS 侧执行 DOM 指令的解释器;unified_bindings.rs 用 sledgehammer_bindgen 生成批量调用的绑定 | unified_bindings.rs 210、ts/core.ts 325、ts/hydrate.ts、ts/initialize_streaming.ts;src/js/*.js 是编译好的单行产物,提交在仓库里 | WebsysDom 与 interpreter、事件委托 |
dioxus-ssr | 922 | 把 VirtualDom 渲染成 HTML 字符串,可带水合标记 | renderer.rs 513(Renderer)、cache.rs 372、lib.rs 35(render、pre_render) | dioxus-ssr |
dioxus-desktop、dioxus-native、dioxus-native-dom、dioxus-liveview | 5 527 / 674 / 924 / 1 797 | 桌面和移动(wry webview)、原生渲染(blitz)、liveview(WebSocket 把指令推给浏览器) | 本卷不讲 | — |
dioxus-ssr 的 src/ 里还有 template.rs 和 config.rs 两个文件,内容只有一个换行;lib.rs 仍然声明了 pub mod config;、pub mod template;,所以 dioxus_ssr::config、dioxus_ssr::template 是两个空模块。dioxus-html 被 web、desktop、native 共用,SSR 不用它。
fullstack:server function、水合与服务端
| package | src/ 行数 | 管什么 | 主要文件(行数) | 讲它的部分 |
|---|---|---|---|---|
dioxus-fullstack | 6 225 | 用户直接用的那一层:server function 的客户端请求、按类型选编码、各种载荷 | client.rs 693(ClientRequest)、magic.rs 852(ServerFnEncoder / ServerFnDecoder)、encoding.rs 120、payloads/ 3 956(WebSocket、流、SSE、文件、表单、msgpack……) | server function |
dioxus-fullstack-core | 2 245 | 客户端和服务端共用的类型:水合数据、use_server_future、ServerFnError、流式状态 | transport.rs 530(HydrationContext)、loader.rs 381、streaming.rs 338、error.rs 314、httperror.rs 217、history.rs 146、server_future.rs 139 | 水合、流式渲染 |
dioxus-fullstack-macro | 1 925 | #[server]、#[get]、#[post] 等宏,一个文件 | lib.rs 1 925 | server function |
dioxus-server(目录 packages/fullstack-server) | 3 171 | axum 集成:路由、SSR 渲染池、流式、增量缓存、启动 | ssr.rs 767(SsrRendererPool)、server.rs 493(DioxusRouterExt、FullstackState)、config.rs 307(ServeConfig)、launch.rs 290(router、serve)、isrg/ 632、document.rs 166、streaming.rs 162、serverfn.rs(ServerFunction) | 一次请求的渲染、增量渲染与配置 |
dioxus-fullstack 的 src/ 里有 63% 是 payloads/:除了默认的 JSON,server function 还能收发流、SSE、WebSocket、文件和表单,这些载荷各自实现客户端和服务端两边的编码。本卷只讲默认路径,载荷只列不展开。
router
| package | src/ 行数 | 管什么 | 主要文件(行数) | 讲它的部分 |
|---|---|---|---|---|
dioxus-router | 2 741 | Router、Outlet、Link 组件,RouterContext,Routable trait | routable.rs 965、contexts/ 581(router.rs 420)、components/ 662(link.rs 266)、navigation.rs 170 | Router 运行时 |
dioxus-router-macro | 2 682 | #[derive(Routable)]:路由树、FromStr、Display | lib.rs 791、route_tree.rs 614、route.rs 469、segment.rs 338、query.rs 174 | Routable |
document 与 history
| package | src/ 行数 | 管什么 | 主要文件(行数) | 讲它的部分 |
|---|---|---|---|---|
dioxus-document | 1 045 | Title、Meta、Link、Script、Style 组件和 eval;Document trait 由各平台实现 | document.rs 152、eval.rs 82、elements/ | document 与 eval |
dioxus-history | 381 | History trait 和 MemoryHistory | lib.rs 261、memory.rs 120 | Router 运行时 |
这两个 crate 都只是 trait 加默认实现。浏览器里的实现在 dioxus-web 的 document.rs 和 history.rs,服务端的 ServerDocument 在 dioxus-server 的 document.rs,服务端的路由历史在 dioxus-fullstack-core 的 history.rs。
工具链:CLI、热重载、资源、代码分割
| package | src/ 行数 | 管什么 |
|---|---|---|
dioxus-cli(dx) | 40 833 | 构建、打包、开发服务器、热重载,是 dioxus-core 的 3.8 倍;本卷只用它编实验,不讲源码 |
dioxus-cli-config | 336 | CLI 通过环境变量传给应用的配置:服务地址、base_path、构建 id |
subsecond、subsecond-types | 959 / 92 | Rust 代码的热补丁:跳转表替换函数,VirtualDom 在 debug 下注册回调 |
dioxus-devtools、dioxus-devtools-types | 220 / 56 | 开发服务器和应用之间的消息(模板替换、补丁) |
dioxus-rsx-hotreload | 1 079 | 比较新旧 rsx!,算出能热替换的模板 |
dioxus-autofmt | 2 225 | dx fmt,基于 dioxus-rsx |
dioxus-rsx-rosetta | 162 | HTML 转 rsx |
manganis、manganis-core、manganis-macro、manganis-core-07 | 620 / 2 005 / 1 910 / 723 | asset!():编译期把资源描述写进二进制的链接段,CLI 处理后回填最终 URL |
const-serialize、const-serialize-macro | 2 264 / 234 | 编译期序列化,asset! 用 |
dioxus-asset-resolver | 583 | 运行时按 URL 找资源 |
wasm-splitter、wasm-split-macro、wasm-split-cli、wasm-used | 214 / 226 / 1 725 / 346 | wasm 代码分割 |
lazy-js-bundle | 194 | 构建脚本里把 TypeScript 编成 JS(interpreter 用) |
dioxus-logger | 116 | 默认的 tracing 订阅者,logger feature 打开时 launch 会初始化它 |
dioxus-config-macro、dioxus-config-macros | 38 / 39 | 前者是 server_only!、web! 这类按 feature 展开的过程宏;后者是 router 宏内部用的 maybe_wasm_split! |
| 其他 | dioxus-cli-telemetry、dioxus-dx-wire-format、depinfo、dioxus-component-manifest、dioxus-html-internal-macro、dioxus-ext(publish = false) |
热重载怎么接进运行时,本卷只讲两个接口:hot-patch 之后的 SchedulerMsg::AllDirty,和 debug 构建里 rsx! 从一个 GlobalSignal 读模板,见怎么读这一卷的 debug 一节。asset! 在本站怎么用,见 10 在本站中。
门面 crate dioxus
packages/dioxus/src 只有两个文件:lib.rs 251 行、launch.rs 373 行。它不实现任何东西,只做三件事:
按 feature 重新导出子 crate。
lib.rs里每个pub use前面都有一个#[cfg(feature = "...")]:feature 导出 总是 dioxus::core(dioxus_core)、dioxus::subsecond、dioxus::config_macrossignalsdioxus::signals、dioxus::storeshooks/html/documentdioxus::hooks/dioxus::html/dioxus::document、dioxus::historyweb/ssr/router/fullstackdioxus::web/dioxus::ssr/dioxus::router/dioxus::fullstackserverdioxus::server(dioxus_server)和dioxus::servedesktop、mobile、native、liveview各自的渲染器 crate 拼一个
prelude。use dioxus::prelude::*带进来的东西也按 feature 分:macro给rsx!、component、Props;hooks和signals给全部 hooks 和 signal 类型;fullstack给server、get、post等宏和use_server_future、ServerFnError;server给serve、DioxusRouterExt、ServeConfig;router给Router、Link、Outlet、Routable、navigator。不受 feature 控制的是从dioxus_core直接拿的一组:VirtualDom、Element、VNode、spawn、use_hook、provide_context、consume_context、ErrorBoundary、SuspenseBoundary等。launch。launch.rs的LaunchBuilder按打开的平台 feature 选一个启动函数,见怎么读这一卷。
dioxus 的默认 feature 是 launch、devtools、logger、lib,lib 又展开成 macro、html、signals、hooks、mounted、document、asset、warnings、cli-config。router、fullstack、web、server 都不在默认集里,要自己开。
小结
- 工作区 112 个成员,其中库和工具 57 个,整体同版本 0.7.10 发布。
- 运行时从下往上:
generational-box→dioxus-core→dioxus-signals→dioxus-hooks→dioxus-html;rsx!在dioxus-rsx里实现、由dioxus-core-macro包成过程宏。 - 渲染器只依赖 core:
dioxus-ssr连dioxus-html都不需要;dioxus-web加上dioxus-interpreter-js(TypeScript 解释器 + sledgehammer 绑定)。 - fullstack 分四个 crate:用户面
dioxus-fullstack、两边共用的dioxus-fullstack-core、宏dioxus-fullstack-macro、axum 集成dioxus-server;fullstack-core的web/serverfeature 由拉它进来的渲染器写死。 dioxus-cli一个 package 就有 4 万行,是dioxus-core的 3.8 倍;本卷不讲它。- 门面 crate
dioxus只做重新导出、prelude和launch,导出什么全看 feature。