首页
/ Dioxus FAQ 技术深潜:VDOM 开销、Wasm 部署、Hooks 与原生 WebView 渲染的设计决策

Dioxus FAQ 技术深潜:VDOM 开销、Wasm 部署、Hooks 与原生 WebView 渲染的设计决策

2026-09-05 11:29:28作者:伍希望

本篇以 Dioxus 仓库中的 FAQ 文档 为核心,逐条拆解这个跨平台 Rust UI 框架背后八个关键设计决策:为什么坚持使用 VDOM 而非 Solid/Svelte 式的细粒度更新、Wasm 与 DOM 交互的真实开销、二进制体积的常见误解、Hooks 与 RSX DSL 的选型理由、构建速度预期,以及移动端/桌面端如何通过系统原生 WebView 渲染页面。读完本文,你可以结合仓库内的基准测试源码(jsframework.rs)、桌面渲染器实现(webview.rs)和核心架构文档(01-CORE.md),把 FAQ 中的每个结论对应到具体代码证据,形成对 Dioxus 架构取舍的完整判断依据。

为什么坚持 VDOM:与 Solid/Svelte 路线的分野

FAQ 的第一个问题直接回应了对 VDOM 的质疑:“VDOM 不是纯开销吗?为什么不用 Solid 或 Svelte?”文档给出的核心论点是三层的:

  1. Dioxus 是库,不是编译器。Svelte 依赖编译期把组件转换掉以消除 VDOM,而 Dioxus 作为 Rust 库(而非编译工具链的一部分),必须保留一套运行时的中间表示。
  2. 内层 VDOM 是可移植性的基石。它让 Dioxus 能轻松移植到不同运行时、支持 SSR、并支持把渲染逻辑放到远端(例如 fullstack 模式下的服务端渲染),这些能力在纯编译器方案中很难获得。
  3. VDOM 更符合 Rust 的书写习惯,整体感觉“很像自然的 Rust 代码”,而其运行时代价“极其微小”(extraordinarily minimal),比真正更新页面所需的时间低一个数量级。

源码证据:基准测试如何定义“开销”

这个结论并非空口无凭。packages/core/benches/jsframework.rs 文件头部的注释精确说明了该基准的测量口径(见 L2-L18):

This benchmark tests just the overhead of Dioxus itself. ... Dioxus prepares changes to be made, but the change application phase will be just as performant as the vanilla wasm_bindgen code. In essence, we are measuring the overhead of Dioxus, not the performance of the "apply" phase.

也就是说,该基准通过向 NoOpMutations 写入变更来只测量 Dioxus 框架层的开销,而把“应用变更到 DOM”这一阶段排除在外——因为那一阶段无论用 Dioxus 还是纯 wasm_bindgen 都一样快。注释中记录了 Mac M1 上的实测数据:引入模板系统(templates)之后,创建 1,000 行从 3ms 降到 580us,创建 10,000 行从 30ms 降到 6.2ms,并且注释承认这些数字还未包含启发式引擎优化。

底层机制:模板系统为什么快

模板机制的实现细节可以从核心架构文档 01-CORE.md 得到印证。VNode 由静态部分与动态部分分离构成:

pub struct Template {
    pub roots: &'static [TemplateNode],
    pub node_paths: &'static [&'static [u8]],
    pub attr_paths: &'static [&'static [u8]],
}

静态结构以 &'static 指针形式存储,diff 时“不同模板 → 整体替换子树”“同指针 → 跳过”等快速路径(见 01-CORE.md 的 Diffing Algorithm 一节)使得大多数比较开销极低;真正变化的只有 DynamicValues 中的动态节点和动态属性。这正是 FAQ 中“overhead 极其微小”结论的工程来源。

Wasm 操作 DOM 的开销有多大?

FAQ 第二个问题指出:Wasm 与 JS API 之间的交互开销“被极度误解”,Rust web 基准测试通常受 Rust 与 JS 字符串缓存机制差异的干扰。Dioxus 声称解决了其中大部分问题,其 JS Framework Benchmark 在很多场景下跑赢了纯 Wasm Bindgen 基准;对比“纯 vanilla JS”方案,Dioxus 增加的开销不到 5%,并利用了批量化的 DOM 变更(batched DOM manipulation)

仓库源码可以从两端印证这一说法:

  • 基准侧jsframework.rs 完整复刻了 keyed 版 js-framework-benchmark 的应用结构(create / append / update every 10th / select / swap / remove / clear 七类操作,见 L134-L210),其组件与 signal 结构与浏览器实现保持一致(L352-L356),保证了测量对象与公开基准可比。
  • 批量侧:Web 渲染器在提交阶段显式调用 websys_dom.flush_edits() 将攒批的变更一次性刷出(见 packages/web/src/lib.rs 中的 flush_edits 调用),这正是 FAQ 提到的“batched DOM manipulation”在代码层面的体现。

Wasm 二进制是不是太大了?

FAQ 用“流式编译”解释了这个广为流传的误解:50kb 的 Wasm 和 50kb 的 JS 并不等价。JavaScript 必须先完整下载、再进行 JIT 编译,50kb 的 JS 的 JIT 代价不可忽略;而 Wasm 的下载与 JIT 编译是同时进行的(streaming compilation),50kb 的 Rust 代码下载完时往往已经可以运行。因此在“可交互时间”(time-to-interactivity)这个真正有意义的指标上,Dioxus 在很多基准中占优。

文档同时给出了具体数字作为参照:Dioxus 的 hello-world 约为 70kb gzip / 60kb brotli,且 Dioxus 支持 SSR 进一步缩短首屏。仓库中的 hello_world.rs 展示了这个最小应用的全部代码:

use dioxus::prelude::*;

fn main() {
    dioxus::launch(app);
}

fn app() -> Element {
    rsx! {
        div { "Hello, world!" }
    }
}

值得注意的适用前提:FAQ 中的体积数字是文档给出的历史参考值,实际大小会随依赖、编译器版本与发布配置浮动,请以自行构建为准。

为什么是 Hooks?为什么不是 MVC / 类 / 消息传递?

FAQ 的立场很明确:Elm 范式的 Rust 框架已经足够多,Dioxus 不打算再造一个;团队直接从 React 借用了 hooks。理由是 JS 与 Rust 有大量结构性相似——如果你熟悉 React,Dioxus 也会让你同样舒适。

hooks 在仓库中的实现集中在 dioxus-hooks crate。packages/hooks/src/lib.rs 暴露了与 React 心智模型对应的能力集:use_contextuse_effectuse_memouse_signaluse_resourceuse_coroutineuse_futureuse_set_compareuse_waker 等。而 hooks 状态在组件 scope 中的存储机制,可以从核心架构看到:每个 Scope 维护一个 hooks: RefCell<Vec<Box<dyn Any>>> 数组和 hook_index: Cell<usize> 游标(见 01-CORE.md 的 Scope System 一节),这正是 React 式“按调用顺序索引 hook 槽位”的 Rust 实现。基准测试中的应用代码也大量使用 hooks——例如 jsframework.rsuse_signal(Vec::<RowData>::new)use_set_compareuse_drop 的组合(L357-L362),说明 hooks + signals 就是 Dioxus 的日常状态管理方式。

为什么要自定义 DSL?不能直接函数调用吗?

FAQ 的回答颇有意味:RSX! 几乎算不上一个 DSL。对 Rustaceans 来说,它非常像“组装嵌套结构体”,只是免除了到处写 Default 和 builder 模式样板的语法负担。文档同时列出了从低到高的完整 API 梯度:RSX → HTML → Raw Factory API → NodeBuilder 语法,开发者可按需选择。

examples/09-reference/ 中的参考示例可以看到这条梯度:html_builder.rs(builder 语法)、rsx_usage.rs(RSX)、custom_element.rs 等各自演示了一种写法。这意味着 RSX 宏不是唯一入口,它只是最常用、最省字面量的入口。

构建时间如何?为什么选 Rust 而不是 JS/TS/Elm?

FAQ 给出的预期管理:dx 的构建速度大约与一个复杂的 WebPack + TypeScript 项目相当;编译时间会比同等规模的 TypeScript 项目慢,但不至于难以忍受。Rust 的 Wasm 后端编译器非常快,小组件的迭代“基本是瞬间完成”,大型应用需要几秒。文档的结论是:Rust 编译器提供的类型保证在实际开发中抵消了重建时间的劣势。

这与 Dioxus CLI 的两段式工作流一致:日常开发用 dx serve 做快速迭代,dx build 做发布构建。构建链路的设计细节可以参考仓库内的架构笔记 02-CLI.md

与 Yew / Seed / Sycamore / Dominator / Dodrio / Percy 的关系

FAQ 用五条要点划清了 Rust web 生态的定位图:

框架 FAQ 中的定位
Yew、Seed Elm 式范式,不支持 SSR 或其他渲染平台
Sycamore、Dominator 更接近 SolidJS/Svelte,无 VDOM,但状态管理不太“Rust 味”
Percy 当时尚不成熟
Dodrio Dioxus 的精神前身,当前是已归档的研究项目,没有 Dioxus 的完整能力(batteries)

注意这是 FAQ 文档撰写时的生态快照,各框架的现状可能已经演进,以上对比仅作为 Dioxus 团队当年的选型参照。

移动端与桌面端如何渲染?是 Electron 吗?

FAQ 明确:不是 Electron。Dioxus 使用设备自带的系统 WebView 来绘制页面,但你的应用代码并不运行在 WebView 线程里,而是以原生编译代码运行在主进程,因此可以直接访问系统资源,而不必像 NodeJS 桥接那样绕路。各平台的实际渲染引擎是:

  • macOS / iOS:Safari(WebKit)
  • Windows:Edge(Chromium)
  • Linux / Android:系统默认浏览器内核

这也带来两个工程上的实际影响:访问浏览器原生 API(如 WebGL)需要通过各种 “Escape Hatches”(逃生舱,即 Dioxus 提供的 navigator/window 等 API 封装);不同引擎对页面的渲染细节存在视觉差异,需要自己适配。文档同时坦陈了当前边界:移动端/桌面端非常适合 CRUD 类应用,但跨平台的友好 API(GPS、相机等)当时还不完善。

源码印证:wry + tao 的 WebView 封装

packages/desktop/src/webview.rs 的头部导入直接揭示了实现基础(L1-L18):

use wry::{DragDropEvent, RequestAsyncResponder, WebContext, WebViewBuilder, WebViewId};
use dioxus_core::{RenderTargetId, Runtime, VirtualDom};

wry 正是跨平台系统 WebView 的抽象层(其 os-webview 特性在 packages/desktop/Cargo.toml 中启用),tao 负责窗口管理。WebviewEdits 结构体持有 runtime: Rc<Runtime>wry_queue,把虚拟 DOM 产生的 mutations 通过队列投递到 WebView——这与 FAQ 描述的“应用代码不在 WebView 里跑、通过队列把变更投给渲染器”的架构一一对应。

FAQ 末尾还预告了演进方向:未来有兴趣使用 WebRenderer 提供不依赖系统 WebView 的完全原生渲染器。

小结:从 FAQ 到代码的验证路径

FAQ 论点 仓库中的验证位置
VDOM 开销极小 packages/core/benches/jsframework.rs(NoOpMutations 只测框架开销)、notes/architecture/01-CORE.md(模板/快速 diff 路径)
批量化 DOM 变更 packages/web/src/lib.rsflush_edits 提交点)
hello-world 体积与 SSR examples/01-app-demos/hello_world.rs
Hooks 选型 packages/hooks/src/lib.rs、Scope 的 hook 槽位存储(01-CORE.md)
原生 WebView 而非 Electron packages/desktop/src/webview.rspackages/desktop/Cargo.toml(wry os-webview

FAQ 的价值在于把“为什么这样设计”讲清楚了;而沿着上表的路径进入源码,可以把每一条设计决策落回具体的类型、基准与提交管线,这正是理解 Dioxus 架构的最短路径。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
590
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
889
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
983
503
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384