Loading...

文章背景图

Dioxus 框架详细教程:与原生 Web 开发对比

2026-03-07
34
-
- 分钟
|

引言

如果你已经熟练掌握 Rust 和原生 Web 开发(HTML、CSS、JavaScript),那么你可能已经对前端开发的种种痛点深有体会:JavaScript 的动态类型导致运行时错误、状态管理容易失控、DOM 操作繁琐且容易引发性能问题、缺乏类型安全的组件化……而 Rust 以其安全性、高性能和卓越的工具链,为前端开发带来了新的可能性。

Dioxus 是一个用于构建跨平台用户界面的 Rust 框架,它借鉴了 React 的思想,但充分利用了 Rust 的语言特性。它可以将你的 Rust 代码编译为 WebAssembly,在浏览器中运行,同时也支持桌面、移动和服务器端渲染。本教程将从你已经熟悉的原生 Web 视角出发,带你快速掌握 Dioxus 的核心概念和用法。

环境搭建

原生 Web

  • 只需要一个文本编辑器和一个浏览器。
  • 可选:Node.js 和 npm 用于构建工具(如 webpack)。

Dioxus

  • Rust 工具链:通过 rustup 安装 Rust,确保 wasm32-unknown-unknown 目标已添加。
    rustup target add wasm32-unknown-unknown
    
  • Dioxus CLI:方便创建、构建和运行项目。
    cargo install dioxus-cli
    
  • 可选:如果你需要与 JavaScript 生态交互,可能需要 wasm-bindgen 等工具。

对比:Dioxus 的工具链完全基于 Rust,无需 Node.js 环境(除非你需要与 npm 包交互)。CLI 提供了类似 create-react-app 的体验,但更轻量。

第一个应用:Hello World

原生 Web

创建一个 index.html

<!DOCTYPE html>
<html>
  <head>
    <title>Hello World</title>
  </head>
  <body>
    <div id="app"></div>
    <script>
      const app = document.getElementById('app');
      app.innerHTML = '<h1>Hello, World!</h1>';
    </script>
  </body>
</html>

使用 Dioxus CLI 创建项目

当你运行 dx new hello-dioxus 时,CLI 会通过一系列交互式问题引导你完成项目初始化。下面详细解释每个选项,以便你根据需求做出选择。

模板选择

? 🤷   Which sub-template should be expanded? ›
❯ Bare-Bones
  Jumpstart
  Workspace
  • Bare-Bones:生成最简项目结构,只包含核心依赖和基础文件。适合完全自定义配置的开发者。
  • Jumpstart:包含一些常用预设,例如可能集成了路由器、CSS 处理等,让你快速开始一个功能更完整的应用。
  • Workspace:创建一个 Cargo 工作区,适合需要管理多个相关包(例如将 UI 和逻辑分离)的项目。

选择不同模板会影响生成的文件和 Cargo.toml 中的依赖。对于初学者,通常建议选择 Bare-BonesJumpstart,后者可以帮你省去一些初始配置。

功能选项

选择模板后,CLI 会继续询问一系列功能选项:

✔ 🤷   Do you want to use Dioxus Fullstack? · false
✔ 🤷   Do you want to use Dioxus Router? · false
✔ 🤷   Do you want to use Tailwind CSS? · false
✔ 🤷   Do you want to include prompts for LLMs? · false
  • Dioxus Fullstack:是否启用全栈功能?启用后,项目会添加 dioxus-fullstack 依赖,允许你在同一应用中编写服务器端代码(如服务器函数),并支持服务端渲染(SSR)。如果你计划构建需要与后端紧密集成的应用,可以选择 true
  • Dioxus Router:是否包含官方路由库 dioxus-router?路由是现代单页应用(SPA)的必备功能,如果你的应用有多个页面,建议启用。
  • Tailwind CSS:是否集成 Tailwind CSS 工具链?如果选择 true,CLI 会生成 tailwind.config.js 和相应的 CSS 文件,并配置构建过程。Tailwind 是一个流行的原子化 CSS 框架,可以大幅提高样式开发效率。
  • Prompts for LLMs:是否包含用于大型语言模型(如 AI 代码生成)的提示文件(如 AGENTS.md)?这些文件为 AI 助手提供项目上下文,如果你打算使用 Cursor、Copilot 等工具辅助开发,可以选择 true

默认平台

✔ 🤷   Which platform do you want DX to serve by default? · Web
  • Web:默认目标为浏览器,生成 WebAssembly 应用。
  • Desktop:默认目标为桌面应用,使用系统 WebView 或原生渲染(需要 dioxus-desktop)。
  • Mobile:默认目标为移动应用(实验性)。

选择后,dx serve 命令将针对该平台启动开发服务器。

项目生成示例

假设我们选择了 Bare-Bones 模板,并禁用了所有功能选项,最终生成的目录结构大致如下:

hello-dioxus/
├── .gitignore
├── AGENTS.md                # 如果启用了 LLM prompts 则生成
├── Cargo.toml               # 项目依赖和配置
├── Dioxus.toml              # Dioxus 项目配置文件(如开发服务器端口、资源路径等)
├── README.md
├── assets/                  # 静态资源文件夹
│   ├── favicon.ico
│   ├── header.svg
│   ├── main.css
│   └── tailwind.css         # 如果启用了 Tailwind 则生成
├── clippy.toml              # Clippy lint 配置
└── src/
    └── main.rs              # 应用入口

Cargo.toml 中会自动添加必要的依赖(如 dioxusdioxus-web 等)。src/main.rs 包含一个简单的 Hello World 组件。

现在,进入项目目录并运行开发服务器:

cd hello-dioxus
dx serve

默认情况下,应用将在 http://localhost:8080 打开。

代码解析

打开 src/main.rs,你会看到:

use dioxus::prelude::*;

fn main() {
    dioxus_web::launch(App);
}

#[component]
fn App() -> Element {
    rsx! {
        h1 { "Hello, World!" }
    }
}

对比原生 Web

  • 我们不再手动操作 DOM(如 document.getElementById),而是通过 rsx! 宏声明 UI。
  • 应用通过 dioxus_web::launch 挂载到 DOM(默认挂载到 body 或指定的元素)。
  • 所有 Rust 代码最终编译为 WASM,在浏览器中运行。

下一步

现在你已经成功创建并运行了第一个 Dioxus 应用,接下来我们将深入核心概念,看看 Dioxus 如何以声明式、类型安全的方式构建交互界面。

核心概念

(以下内容与之前相同,但我们可以保持原样,因为用户没有指出问题)

1. 组件化

原生 Web:可以使用原生 Web Components,但需要编写较多样板代码,且与框架的响应式系统集成不自然。或者手动创建元素、添加事件监听,容易导致代码混乱。

Dioxus:组件是返回 Element 的 Rust 函数,可以通过 #[component] 属性标记,并接受 Props

#[derive(Props, PartialEq, Clone)]
struct WelcomeProps {
    name: String,
}

#[component]
fn Welcome(props: WelcomeProps) -> Element {
    rsx! {
        div { "Hello, ", props.name }
    }
}

// 使用
rsx! { Welcome { name: "Alice" } }

对比

  • Props 是类型安全的:你必须传递正确类型和名称的属性,否则编译失败。
  • 组件默认是纯函数,依赖 props 和内部状态。
  • 没有 this 绑定问题,一切都是 Rust 的所有权和借用规则。

2. 状态管理

原生 Web:需要手动维护状态,并在变化时更新 DOM。例如:

let count = 0;
const button = document.querySelector('button');
button.addEventListener('click', () => {
  count++;
  button.textContent = `Count: ${count}`;
});

随着状态增多,更新逻辑分散,容易出错。

Dioxus:使用 Hooks 管理状态,最常见的是 use_state

#[component]
fn Counter() -> Element {
    let mut count = use_state(|| 0);

    rsx! {
        button {
            onclick: move |_| count += 1,
            "Count: {count}"
        }
    }
}

对比

  • use_state 返回一个智能指针(类似 Rc),通过 Deref 实现读取,通过 set 方法或 += 运算符(实现了 std::ops::AddAssign)更新。
  • 当状态变化时,Dioxus 自动重新运行组件函数,生成新的虚拟 DOM,并高效地更新真实 DOM。
  • 避免了手动 DOM 操作,状态与 UI 自动同步。

3. 事件处理

原生 Web:使用 addEventListeneronclick 属性。回调函数中操作 DOM 或状态。

Dioxus:在 rsx! 中直接添加事件监听器,闭包会捕获环境中的状态。

rsx! {
    button {
        onclick: move |event| {
            println!("Clicked at: {:?}", event.client_coordinates());
            count += 1;
        },
        "Click me"
    }
}

对比

  • 事件类型是强类型的(如 MouseEvent),可以访问坐标、按键等。
  • 闭包是 'static 的,需要注意移动捕获(使用 move 关键字)以避免生命周期问题。
  • 事件处理函数中可以直接修改状态(通过 set+=),触发重新渲染。

4. 生命周期与副作用

原生 Web:使用 window.addEventListener('load', ...)DOMContentLoaded 来执行初始化;使用 setInterval 等需要手动清理,容易导致内存泄漏。

Dioxususe_effect Hook 用于处理副作用,可以指定依赖项,并在组件卸载时自动清理。

use_effect(|| {
    // 副作用代码,例如订阅事件
    let interval = set_interval(|| {
        // do something
    }, 1000);

    // 返回一个清理函数(可选)
    move || {
        clear_interval(interval);
    }
}, deps); // 依赖项数组,当依赖变化时重新运行

对比

  • 依赖项明确控制副作用的执行时机,避免不必要的重复执行。
  • 清理函数在组件卸载或副作用重新运行前调用,防止内存泄漏。

5. 列表和条件渲染

原生 Web:需要手动创建、插入、删除 DOM 节点。例如:

const list = document.getElementById('list');
items.forEach(item => {
  const li = document.createElement('li');
  li.textContent = item;
  list.appendChild(li);
});

条件渲染则需要 if 语句和 removeChild

Dioxus:在 rsx! 中可以使用 Rust 的迭代器和条件表达式。

#[component]
fn ItemList(items: Vec<String>) -> Element {
    rsx! {
        ul {
            {items.iter().map(|item| rsx! { li { "{item}" } })}
        }
        if items.is_empty() {
            p { "No items" }
        }
    }
}

对比

  • 直接在 rsx! 中使用 Rust 表达式生成元素,无需手动 DOM 操作。
  • Dioxus 的 diffing 算法会自动更新列表(通过 key 属性优化)。
  • 条件渲染就是 Rust 的 if 表达式,非常自然。

6. 样式

原生 Web:使用 CSS 文件、内联样式或 CSS-in-JS 库。

Dioxus:支持多种方式:

  • 内联样式:通过 style 属性传入字符串或键值对。
    div { style: "color: red; background-color: black;", "Styled" }
    
  • CSS 类:通过 class 属性。
    div { class: "container", "Content" }
    
  • CSS-in-Rust:可以使用 dioxus-free-css 等库在 Rust 中编写样式,编译为 CSS。
  • 全局 CSS:在 index.html 中引入传统 CSS 文件(通过 assets/ 文件夹)。

对比

  • 样式隔离:组件默认不会互相影响,除非使用全局类。
  • 类型安全:可以通过类型定义限制类名,避免拼写错误(配合 dioxus-free-css 等库)。

7. 路由

原生 Web:使用 History API 手动管理 URL,监听 popstate 事件,渲染相应内容。

Dioxus:提供 dioxus-router 库,声明式路由。

use dioxus_router::prelude::*;

#[derive(Routable, Clone)]
enum Route {
    #[route("/")]
    Home {},
    #[route("/about")]
    About {},
    #[route("/users/:id")]
    User { id: String },
}

#[component]
fn App() -> Element {
    rsx! {
        Router::<Route> {}
    }
}

然后在每个路由对应的组件中渲染内容。

对比

  • 路由类型安全:路由枚举确保你只能访问定义的路径。
  • 嵌套路由、链接、重定向等常见功能开箱即用。
  • 与组件集成自然,无需手动监听 URL。

8. 与 JavaScript 互操作

原生 Web:JavaScript 直接调用 Web API。

Dioxus:通过 web-sysjs-sys crate 调用浏览器 API。这些 crate 提供了对 Web API 的 Rust 绑定,类型安全。

use web_sys::window;

let window = window().unwrap();
window.alert_with_message("Hello from Rust!").unwrap();

如果需要调用自定义 JavaScript 函数,可以使用 wasm-bindgen#[wasm_bindgen] 属性。

对比

  • 几乎所有 Web API 都有 Rust 绑定,可以安全调用。
  • 如果需要使用 npm 包,可以通过 wasm-bindgen 导入,但需要额外配置(如 wasm-packtrunk)。

高级特性

服务器函数(全栈)

Dioxus 支持服务器端渲染(SSR)和全栈开发。你可以定义服务器函数,在客户端调用,自动处理序列化和网络请求。

#[server(GetServerData)]
async fn get_server_data() -> Result<String, ServerFnError> {
    // 服务器端代码
    Ok("Data from server".to_string())
}

#[component]
fn App() -> Element {
    let data = use_server_future(get_server_data())?;

    rsx! {
        div { "{data}" }
    }
}

对比原生:原生需要手动编写 API 路由、fetch 请求、处理 JSON 序列化/反序列化。Dioxus 服务器函数将这一切抽象化,类型安全且简洁。

跨平台开发

Dioxus 不仅支持 Web,还支持桌面(通过 dioxus-desktop,基于 Tauri)和移动(通过 dioxus-mobile)。代码可以共享,只需替换渲染器。

// 桌面应用
fn main() {
    dioxus_desktop::launch(App);
}

对比原生:原生 Web 只运行在浏览器中;Dioxus 允许你用同一套代码构建桌面和移动应用,类似于 Electron 但更轻量(使用系统 webview 或原生渲染)。

构建和部署

原生 Web

通常使用 webpack、vite 等工具打包,部署静态文件到服务器或 CDN。

Dioxus

使用 Dioxus CLI 构建:

dx build --release

输出目录为 dist,包含 WASM 文件、JS 胶水代码和 HTML。你可以将这些文件部署到任何静态文件服务器。

对比

  • Dioxus 构建产物体积相对较小(WASM 二进制可能比 JS 包大,但可以通过优化减小)。
  • 无需配置复杂的构建工具(如 webpack),CLI 已封装好。

总结

Dioxus 将 Rust 的安全、性能和类型系统带入前端开发,同时借鉴了 React 的声明式编程模型。与原生 Web 相比,Dioxus 提供了:

  • 类型安全:组件、状态、事件、路由等都在编译时检查。
  • 声明式 UI:用 rsx! 描述界面,状态自动驱动更新。
  • 自动 DOM 更新:虚拟 DOM diffing 算法优化性能。
  • 跨平台:同一套代码可运行在 Web、桌面、移动。
  • 全栈能力:服务器函数简化前后端通信。

对于已经熟悉 Rust 和原生 Web 的开发者,Dioxus 的学习曲线主要在于理解其 Hooks 系统和响应式模型,而这些与 React 非常相似。相信通过本教程的对比,你已经能够快速上手并开始构建自己的 Dioxus 应用。

评论交流

文章目录