8 个版本
0.6.0-alpha.2 | 2024 年 8 月 11 日 |
---|---|
0.6.0-alpha.0 | 2024 年 8 月 1 日 |
0.5.6 | 2024 年 7 月 18 日 |
0.5.1 | 2024 年 4 月 5 日 |
0.5.0-alpha.0 | 2024 年 2 月 23 日 |
#947 在 GUI 中排名
9,028 每月下载量
在 24 个包中(6 个直接) 使用
585KB
10K SLoC
🌗🚀 Dioxus (lib)
适用于 Rust 的并发、函数式、虚拟 DOM
该包实现了 dioxus-lib,这是 Dioxus 的无渲染器版本。这个包旨在供需要稳定核心版本的 dioxus 且不希望意外引入与渲染器相关依赖的库作者使用。
资源
本概述简要介绍了 Dioxus。要获取更深入的指南,请参阅
概述和目标
Dioxus 使得使用 Rust 快速构建复杂用户界面变得容易。任何 Dioxus 应用都可以在网页浏览器、桌面应用程序、移动应用程序或任何其他地方运行,只要您构建了正确的渲染器。
Dioxus 受 React 强烈启发,支持许多相同的概念
- 状态钩子
- 虚拟 DOM 及其差异算法
- 并发、纤程和异步渲染
- 类似于 JSX 的模板语法
如果您熟悉 React,那么您也会喜欢 Dioxus。
Dioxus 比 Rust 中许多其他 UI 库(Yew/Percy)有更高的性能,并且比 React 有显著的性能提升——大致与 InfernoJS 相当。
请记住:Dioxus 是一个用于声明交互式用户界面的库——它不是一个专用的渲染器。目前 Dioxus 的所有一级渲染器仅支持 Web 技术。
简要概述
所有 Dioxus 应用都是通过组合返回 Element
的函数来构建的。
要启动应用,我们使用 launch
方法,并在 Cargo.toml
中使用特性指定我们想要使用的渲染器。在启动函数中,我们传递应用的根 Component
。
use dioxus::prelude::*;
fn main() {
launch(App);
}
// The #[component] attribute streamlines component creation.
// It's not required, but highly recommended. For example, UpperCamelCase components will not generate a warning.
#[component]
fn App() -> Element {
rsx! { "hello world!" }
}
元素与您的第一个组件
使用Dioxus组装UI树时,您需要在被称为LazyNodes
的东西上使用render
函数。要生成LazyNodes
,您可以使用rsx!
宏或NodeFactory API。大多数情况下,您希望使用rsx!
宏。
rsx!
中的任何元素都可以有属性、监听器和子元素。为了保持一致性,我们强制所有属性和监听器在子元素之前列出。
let value = "123";
rsx! {
div {
class: "my-class {value}", // <--- attribute
onclick: move |_| info!("clicked!"), // <--- listener
h1 { "hello world" }, // <--- child
}
}
rsx!
宏接受“结构形式”的属性。任何实现IntoIterator<Item = impl IntoVNode>
的Rust表达式都将被解析为子元素。我们有两个例外:for循环和if语句的体被解析为子元素。
rsx! {
div {
for _ in 0..10 {
span { "hello world" }
}
}
}
rsx!
宏生成我们的组件返回的Element
。
#[component]
fn Example() -> Element {
rsx!{ "hello world" }
}
将所有这些放在一起,我们可以编写一个简单的组件,该组件渲染一个元素列表
#[component]
fn App() -> Element {
let name = "dave";
rsx! {
h1 { "Hello, {name}!" }
div { class: "my-class", id: "my-id",
for i in 0..5 {
div { "FizzBuzz: {i}" }
}
}
}
}
组件
我们可以组合这些函数组件来构建一个复杂的应用。我们设计的每个新组件都必须接受一些属性。对于没有显式属性的组件,我们可以完全省略类型。
Dioxus默认对所有属性进行记忆化,并实现了Clone和PartialEq。对于无法克隆的props,只需将字段包裹在ReadOnlySignal中,Dioxus将为您处理包装。
#[component]
fn App() -> Element {
rsx! {
Header {
title: "My App",
color: "red",
}
}
}
我们的Header
组件接受一个title
和一个color
属性,我们在显式的HeaderProps
结构中声明这些属性。
// The `Props` derive macro lets us add additional functionality to how props are interpreted.
#[derive(Props, PartialEq)]
struct HeaderProps {
title: String,
color: String,
}
#[component]
fn Header(props: HeaderProps) -> Element {
rsx! {
div {
background_color: "{props.color}"
h1 { "{props.title}" }
}
}
}
使用#[component]
宏还可以从函数参数推导属性结构体
#[component]
fn Header(title: String, color: String) -> Element {
rsx! {
div {
background_color: "{color}"
h1 { "{title}" }
}
}
}
以大写字母开头的组件可以使用传统的(对于React)大括号语法调用,如下所示
rsx! {
Header { title: "My App" }
}
钩子
虽然组件是UI元素的重复使用形式,但钩子是逻辑的重复使用形式。钩子为我们提供了一种从Dioxus内部Scope
检索状态并将其用于渲染UI元素的方法。
按照惯例,所有钩子都是以use_
开头应调用的函数。我们可以使用钩子定义状态并在监听器中修改它。
#[component]
fn App() -> Element {
let name = use_signal(|| "world");
rsx! { "hello {name}!" }
}
钩子对它们的使用很敏感。要使用钩子,您必须遵守“钩子规则”
- 带有“use_”的函数不应在回调中调用
- 带有“use_”的函数不应按顺序调用
- 带有“use_”的函数不应在循环或条件语句中调用
从某种意义上说,钩子让我们可以在不显式声明状态结构体的情况下向组件添加一个状态字段。然而,这意味着我们需要按正确的顺序“加载”这个结构体。如果这个顺序错了,那么钩子将选择错误的状态并panic。
您将编写的大多数钩子都是其他钩子的组合
fn use_username(d: Uuid) -> bool {
let users = use_context::<Users>();
users.get(&id).map(|user| user.logged_in).ok_or(false)
}
要创建全新的基础钩子,我们可以使用use_hook
方法。
fn use_mut_string() -> String {
use_hook(|_| "Hello".to_string())
}
如果您想扩展Dioxus以添加一些新功能,您可能需要从头开始实现一个新的钩子。
综合以上所述
使用组件、模板和钩子,我们可以构建一个简单的应用。
use dioxus::prelude::*;
fn main() {
launch(App);
}
#[component]
fn App() -> Element {
let mut count = use_signal(|| 0);
rsx!(
div { "Count: {count}" }
button { onclick: move |_| count += 1, "Increment" }
button { onclick: move |_| count -= 1, "Decrement" }
)
}
功能
本概述并未涵盖所有内容。请确保查看官方网站上的教程和参考指南以获取更多详细信息。
除了本概述之外,Dioxus 还支持:
- 服务器端渲染
- 并发渲染(支持异步操作)
- Web/Desktop/移动端支持
- 预渲染和重新激活
- 片段、门和悬念
- 内联样式
- 自定义事件处理器
- 自定义元素
- 基本细粒度响应性(IE SolidJS/Svelte)
- 等等!
构建酷炫的东西 ✌️
依赖关系
~2.5–8.5MB
~73K SLoC