Merge branch 'master' of github.com:DioxusLabs/dioxus

This commit is contained in:
Jonathan Kelley 2022-07-18 13:50:11 -07:00
commit dbb842e285
98 changed files with 4292 additions and 68 deletions

View file

@ -34,8 +34,6 @@
</a>
</div>
<div align="center">
<h3>
<a href="https://dioxuslabs.com"> Website </a>
@ -45,10 +43,11 @@
<a href="https://dioxuslabs.com/guide"> Guide </a>
<span> | </span>
<a href="https://github.com/DioxusLabs/dioxus/blob/master/notes/README/ZH_CN.md"> 中文 </a>
<span> | </span>
<a href="https://github.com/DioxusLabs/dioxus/blob/master/translations/pt-br/README.md"> PT-BR </a>
</h3>
</div>
<br/>
Dioxus is a portable, performant, and ergonomic framework for building cross-platform user interfaces in Rust.
@ -70,6 +69,7 @@ Dioxus can be used to deliver webapps, desktop apps, static sites, mobile apps,
If you know React, then you already know Dioxus.
### Unique features:
- Desktop apps running natively (no Electron!) in less than 10 lines of code.
- Incredibly ergonomic and powerful state management.
- Comprehensive inline documentation - hover and guides for all HTML elements, listeners, and events.
@ -89,14 +89,12 @@ If you know React, then you already know Dioxus.
<tr>
</table>
## Example Projects:
| File Navigator (Desktop) | WiFi scanner (Desktop) | TodoMVC (All platforms) | E-commerce w/ Tailwind (SSR/LiveView) |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [![File Explorer](https://github.com/DioxusLabs/example-projects/raw/master/file-explorer/image.png)](https://github.com/DioxusLabs/example-projects/blob/master/file-explorer) | [![Wifi Scanner Demo](https://github.com/DioxusLabs/example-projects/raw/master/wifi-scanner/demo_small.png)](https://github.com/DioxusLabs/example-projects/blob/master/wifi-scanner) | [![TodoMVC example](https://github.com/DioxusLabs/example-projects/raw/master/todomvc/example.png)](https://github.com/DioxusLabs/example-projects/blob/master/todomvc) | [![E-commerce Example](https://github.com/DioxusLabs/example-projects/raw/master/ecommerce-site/demo.png)](https://github.com/DioxusLabs/example-projects/blob/master/ecommerce-site) |
See the [awesome-dioxus](https://github.com/DioxusLabs/awesome-dioxus) page for a curated list of content in the Dioxus Ecosystem.
## Why Dioxus and why Rust?
@ -105,7 +103,7 @@ TypeScript is a fantastic addition to JavaScript, but it's still fundamentally J
By using Rust, we gain:
- Static types for *every* library
- Static types for _every_ library
- Immutability by default
- A simple and intuitive module system
- Integrated documentation (`go to source` _actually goes to source_)
@ -127,6 +125,7 @@ Specifically, Dioxus provides us many other assurances:
And much more. Dioxus makes Rust apps just as fast to write as React apps, but affords more robustness, giving your frontend team greater confidence in making big changes in shorter time.
## Why NOT Dioxus?
You shouldn't use Dioxus if:
- You don't like the React Hooks approach to frontend
@ -135,6 +134,7 @@ You shouldn't use Dioxus if:
- You need a Send+Sync UI solution (Dioxus is not currently thread-safe)
## Comparison with other Rust UI frameworks
Dioxus primarily emphasizes **developer experience** and **familiarity with React principles**.
- [Yew](https://github.com/yewstack/yew): prefers the elm pattern instead, no borrowed props, supports SSR (no hydration), no direct desktop/mobile support.
@ -143,7 +143,6 @@ Dioxus primarily emphasizes **developer experience** and **familiarity with Reac
- [Dominator](https://github.com/Pauan/rust-dominator): Signal-based zero-cost alternative, less emphasis on community and docs.
- [Azul](https://azul.rs): Fully native HTML/CSS renderer for desktop applications, no support for web/ssr
## Parity with React & Roadmap
Dioxus is heavily inspired by React, but we want your transition to feel like an upgrade. Dioxus is _most_ of the way there, but missing a few key features. These include:
@ -174,7 +173,7 @@ Want to jump in and help build the future of Rust frontend? There's plenty of pl
This project is licensed under the [MIT license].
[MIT license]: https://github.com/DioxusLabs/dioxus/blob/master/LICENSE-MIT
[mit license]: https://github.com/DioxusLabs/dioxus/blob/master/LICENSE-MIT
### Contribution

View file

@ -7,6 +7,11 @@ language = "en"
[language.en]
name = "English"
[language.pt-br]
name = "Português Brasileiro"
title = "Documentação do Dioxus"
description = "Introdução ao Dioxus, um framework portátil, de alto desempenho e ergonômico para criar interfaces de usuário multiplataforma em Rust."
[rust]
edition = "2018"

View file

@ -33,11 +33,11 @@
- [Desktop](publishing/desktop.md)
- [Web](publishing/web.md)
-----------
---
- [Custom Renderer](custom_renderer/index.md)
-----------
---
[Roadmap](roadmap.md)
[Contributing](contributing.md)

View file

@ -0,0 +1,63 @@
# VNodes with RSX, HTML, and NodeFactory
Many modern frameworks provide a domain-specific-language for declaring user-interfaces. In the case of React, this language extension is called JSX and must be handled through additional dependencies and pre/post processors to transform your source code. With Rust, we can simply provide a procedural macro in the Dioxus dependency itself that mimics the JSX language.
With Dioxus, we actually ship two different macros a macro that mimics JSX (the `html!` macro) and a macro that mimics Rust's native nested-struct syntax (the `rsx!` macro). These macros simply transform their inputs into NodeFactory calls.
For instance, this html! call:
```rust
html!(<div> "hello world" </div>)
```
becomes this NodeFactory call:
```rust
|f| f.element(
dioxus_elements::div, // tag
[], // listeners
[], // attributes
[f.static_text("hello world")], // children
None // key
)
```
The NodeFactory API is fairly ergonomic, making it a viable option to use directly. The NodeFactory API is also compile-time correct and has incredible syntax highlighting support. We use what Rust calls a "unit type" the `dioxus_elements::div` and associated methods to ensure that a `div` can only have attributes associated with `div`s. This lets us tack on relevant documentation, autocomplete support, and jump-to-definition for methods and attributes.
![Compile time correct syntax](../images/compiletimecorrect.png)
## html! macro
The html! macro supports a limited subset of the html standard. Rust's macro parsing tools are somewhat limited, so all text between tags _must be quoted_.
However, writing HTML by hand is a bit tedious IDE tools for Rust don't support linting/autocomplete/syntax highlighting. We suggest using RSX it's more natural for Rust programs and _does_ integrate well with Rust IDE tools.
```rust
let name = "jane";
let pending = false;
let count = 10;
dioxus::ssr::render_lazy(html! {
<div>
<p> "Hello, {name}!" </p>
<p> "Status: {pending}!" </p>
<p> "Count {count}!" </p>
</div>
});
```
## rsx! macro
The rsx! macro is a VNode builder macro designed especially for Rust programs. Writing these should feel very natural, much like assembling a struct. VSCode also supports these with code folding, bracket-tabbing, bracket highlighting, section selecting, inline documentation, GOTO definition, and refactoring support.
When helpful, the Dioxus VSCode extension provides a way of converting a selection of HTML directly to RSX, so you can import templates from the web directly into your existing app.
It's also a bit easier on the eyes than HTML.
```rust
dioxus::ssr::render_lazy(rsx! {
div {
p {"Hello, {name}!"}
p {"Status: {pending}!"}
p {"Count {count}!"}
}
});
```
In the next section, we'll cover the `rsx!` macro in more depth.

View file

@ -0,0 +1,243 @@
# Thinking in Reactively
We've finally reached the point in our tutorial where we can talk about the theory of Reactivity. We've talked about defining a declarative view, but not about the aspects that make our code *reactive*.
Understanding the theory of reactive programming is essential to making sense of Dioxus and writing effective, performant UIs.
In this section, we'll talk about:
- One-way data flow
- Modifying data
- Forcing renders
- How renders propagate
This section is a bit long, but worth the read. We recommend coffee, tea, and/or snacks.
## Reactive Programming
Dioxus is one of a handful of Rust libraries that provide a "Reactive Programming Model". The term "Reactive programming" is a classification of programming paradigm much like functional or imperative programming. This is a very important distinction since it affects how we *think* about our code.
Reactive programming is a programming model concerned with deriving computations from asynchronous data flow. Most reactive programs are comprised of datasources, intermediate computations, and a final result.
We consider the rendered GUI to be the final result of our Dioxus apps. The datasources for our apps include local and global state.
For example, the model presented in the figure below is comprised of two data sources: time and a constant. These values are passed through our computation graph to achieve a final result: `g`.
![Reactive Model](https://upload.wikimedia.org/wikipedia/commons/thumb/e/e9/Reactive_programming_glitches.svg/440px-Reactive_programming_glitches.svg.png)
Whenever our `seconds` variable changes, we will then reevaluate the computation for `t`. Because `g` relies on `t`, we will also reevaluate its computation too. Notice that we would've reevaluated the computation for `g` even if `t` didn't change because `seconds` is used to calculate `g`.
However, if we somehow changed our constant from `1` to `2`, then we need to reevaluate `t`. If, for whatever reason, this change did not affect the result of `t`, then we wouldn't try to reevaluate `g`.
In Reactive Programming, we don't think about whether or not we should reevaluate `t` or `g`; instead, we simply provide functions of computation and let the framework figure out the rest for us.
In Rust, our reactive app would look something like:
```rust
fn compute_g(t: i32, seconds: i32) -> bool {
t > seconds
}
fn compute_t(constant: i32, seconds: i32) -> i32 {
constant + seconds
}
fn compute_graph(constant: i32, seconds: i32) -> bool {
let t = compute_t(constant, seconds);
let g = compute_g(t, seconds);
g
}
```
## How is Dioxus Reactive?
The Dioxus VirtualDom provides us a framework for reactive programming. When we build apps with dioxus, we need to provide our own datasources. This can be either initial props or some values fetched from the network. We then pass this data through our app into components through properties.
If we represented the reactive graph presented above in Dioxus, it would look very similar:
```rust
// Declare a component that holds our datasources and calculates `g`
fn RenderGraph(cx: Scope) -> Element {
let seconds = use_datasource(SECONDS);
let constant = use_state(&cx, || 1);
cx.render(rsx!(
RenderG { seconds: seconds }
RenderT { seconds: seconds, constant: constant }
))
}
// "calculate" g by rendering `t` and `seconds`
#[inline_props]
fn RenderG(cx: Scope, seconds: i32) -> Element {
cx.render(rsx!{ "There are {seconds} seconds remaining..." })
}
// calculate and render `t` in its own component
#[inline_props]
fn RenderT(cx: Scope, seconds: i32, constant: i32) -> Element {
let res = seconds + constant;
cx.render(rsx!{ "{res}" })
}
```
With this app, we've defined three components. Our top-level component provides our datasources (the hooks), computation nodes (child components), and a final value (what's "rendered").
Now, whenever the `constant` changes, our `RenderT` component will be re-rendered. However, if `seconds` doesn't change, then we don't need to re-render `RenderG` because the input is the same. If `seconds` *does* change, then both RenderG and RenderT will be reevaluated.
Dioxus is "Reactive" because it provides this framework for us. All we need to do is write our own tiny units of computation and Dioxus figures out which components need to be reevaluated automatically.
These extra checks and algorithms add some overhead, which is why you see projects like [Sycamore](http://sycamore-rs.netlify.app) and [SolidJS](http://solidjs.com) eliminating them altogether. Dioxus is *really* fast, so we're willing to exchange the added overhead for improved developer experience.
## How do we update values in our dataflow graph?
Dioxus will automatically figure out how to regenerate parts of our app when datasources change. But how exactly can we update our data sources?
In Dioxus there are two datasources:
1. Local state in `use_hook` and all other hooks
2. Global state through `provide_context`.
Technically, the root props of the VirtualDom are a third datasource, but since we cannot modify them, they are not worth talking about.
### Local State
For local state in hooks, Dioxus gives us the `use_hook` method which returns an `&mut T` without any requirements. This means raw hook values are not tracked by Dioxus. In fact, we could write a component that modifies a hook value directly:
```rust
fn app(cx: Scope) -> Element {
let mut count = cx.use_hook(|_| 0);
cx.render(rsx!{
button {
onclick: move |_| *count += 1,
"Count: {count}"
}
})
}
```
However, when this value is written to, the component does not know to be reevaluated. We must explicitly tell Dioxus that this component is "dirty" and needs to be re-rendered. This is done through the `cx.needs_update` method:
```rust
button {
onclick: move |_| {
*count += 1;
cx.needs_update();
},
"Count: {count}"
}
```
Now, whenever we click the button, the value will change and the component will be re-rendered.
> Re-rendering is when Dioxus calls your function component *again*. Component functions will be called over and over throughout their lifetime, so they should be mostly side-effect free.
### Understand this!
Your component functions will be called ("rendered" in our lingo) for as long as the component is present in the tree.
A single component will be called multiple times, modifying its own internal state or rendering new nodes with new values from its properties.
### App-Global State
With the `provide_context` and `consume_context` methods on `Scope`, we can share values to descendants without having to pass values through component props. This has the side-effect of making our datasources less obvious from a high-level perspective, but it makes our components more modular within the same codebase.
To make app-global state easier to reason about, Dioxus makes all values provided through `provide_context` immutable. This means any library built on top of `provide_context` needs to use interior mutability to modify shared global state.
In these cases, App-Global state needs to manually track which components need to be re-generated.
To regenerate *any* component in your app, you can get a handle to the Dioxus' internal scheduler through `schedule_update_any`:
```rust
let force_render = cx.schedule_update_any();
// force a render of the root component
force_render(ScopeId(0));
```
## What does it mean for a component to "re-render"?
In our guides, we frequently use the phrase "re-render" to describe updates to our app. You'll often hear this paired with "preventing unnecessary re-renders." But what exactly does this mean?
When we call `dioxus::desktop::launch`, Dioxus will create a new `Scope` object and call the component we gave it. Our `rsx!` calls will create new nodes which we return back to the VirtualDom. Dioxus will then look through these nodes for child components, call their functions, and so on until every component has been "rendered." We consider these nodes "rendered" because they were created because of our explicit actions.
The tree of UI that dioxus creates will roughly look like the tree of components presented earlier:
![Tree of UI](../images/component_tree.png)
But what happens when we call `needs_update` after modifying some important state? Well, if Dioxus called our component's function again, then we would produce new, different nodes. In fact, this is exactly what Dioxus does!
At this point, we have some old nodes and some new nodes. Again, we call this "rendering" because Dioxus had to create new nodes because of our explicit actions. Any time new nodes get created, our VirtualDom is being "rendered."
These nodes are stored in an extremely efficient memory allocator called a "bump arena." For example, a div with a handler and attribute would be stored in memory in two locations: the "old" tree and the "new" tree.
![Bump Arenas](../images/oldnew.png)
From here, Dioxus computes the difference between these trees and updates the Real DOM to make it look like the new version of what we've declared.
![Diffing](../images/diffing.png)
## Suppressing Renders
So, we know how to make Dioxus render, but how do we *stop* it? What if we *know* that our state didn't change and we shouldn't render and diff new nodes because they'll be exactly the same as the last time?
In these cases, you want to reach for *memoization*. In Dioxus, memoization involves preventing a component from rendering again if its props didn't change since the last time it attempted to render.
Visually, you can tell that a component will only re-render if the new value is sufficiently different than the old one.
| props.val | re-render |
| --------- | --------- |
| 10 | true |
| 20 | true |
| 20 | false |
| 20 | false |
| 10 | true |
| 30 | false |
This is why when you `derive(Props)`, you must also implement the `PartialEq` trait. To override the memoization strategy for a component, you can simply implement your own PartialEq.
```rust
struct CustomProps {
val: i32,
}
impl PartialEq for CustomProps {
fn partial_eq(&self, other: &Self) -> bool {
// we don't render components that have a val less than 5
if other.val > 5 && self.val > 5{
self.val == other.val
}
}
}
```
However, for components that borrow data, it doesn't make sense to implement PartialEq since the actual references in memory might be different.
You can technically override this behavior by implementing the `Props` trait manually, though it's unsafe and easy to mess up:
```rust
unsafe impl Properties for CustomProps {
fn memoize(&self, other &Self) -> bool {
self != other
}
}
```
TLDR:
- Dioxus checks if props changed between renders
- If props changed according to PartialEq, Dioxus re-renders the component
- Props that have a lifetime (ie `<'a>`) will always be re-rendered
## Wrapping Up
Wow, that was a lot of material!
Let's see if we can recap what was presented:
- Reactive programming calculates a final value from datasources and computation
- Dioxus is "reactive" since it figures out which computations to check
- `schedule_update` must be called to mark a component as dirty
- dirty components will be re-rendered (called multiple times) to produce a new UI
- Renders can be suppressed with memoization
This theory is crucial to understand how to compose components and how to control renders in your app.

View file

@ -0,0 +1,26 @@
## JavaScript Handlers
Instead of passing a closure, you can also pass a string to event handlers this lets you use JavaScript (if your renderer can execute JavaScript):
```rust
{{#include ../../examples/event_javascript.rs:rsx}}
```
#![allow(non_snake_case)]
use dioxus::prelude::*;
fn main() {
dioxus::desktop::launch(App);
}
fn App(cx: Scope) -> Element {
cx.render(rsx! {
// ANCHOR: rsx
div {
onclick: "alert('hello world')",
}
// ANCHOR_END: rsx
})
}

View file

@ -0,0 +1,38 @@
# Managing State
Every app you'll build with Dioxus will have some sort of state that needs to be maintained and updated as your users interact with it. However, managing state can be particularly challenging at times, and is frequently the source of bugs in many GUI frameworks.
In this chapter, we'll cover the various ways to manage state, the appropriate terminology, various patterns, and some problems you might run into.
## The Problem
Why do people say state management is so difficult? What does it mean?
Generally, state management is the code you need to write to ensure that your app renders the *correct* content. If the user inputs a name, then you need to display the appropriate response like alerts, validation, and disable/enable various elements on the page. Things can quickly become tricky if you need loading screens and cancellable tasks.
For the simplest of apps, all of your state can enter the app from the root props. This is common in server-side rendering we can collect all of the required state *before* rendering the content.
```rust
let all_content = get_all_content().await;
let output = dioxus::ssr::render_lazy(rsx!{
div {
RenderContent { content: all_content }
}
});
```
With this incredibly simple setup, it is highly unlikely that you'll have rendering bugs. There simply is barely any state to manage.
However, most of your apps will store state inside of the Dioxus VirtualDom either through local state or global state.
## Your options
To deal with complexity, you have a couple of options:
- Refactor state out of shared state and into reusable components and hooks.
- Lift state upwards to be spread across multiple components (fan out).
- Use the Context API to share state globally.
- Use a dedicated state management solution like Fermi.

View file

@ -71,4 +71,4 @@ fn app(cx: Scope) -> Element {
}
})
}
```
```

View file

@ -106,4 +106,4 @@ async fn app_endpoint() -> Html<String> {
And that's it!
> You might notice that you cannot hold the VirtualDom across an await point. Dioxus is currently not ThreadSafe, so it _must_ remain on the thread it started. We are working on loosening this requirement.
> You might notice that you cannot hold the VirtualDom across an await point. Dioxus is currently not ThreadSafe, so it _must_ remain on the thread it started. We are working on loosening this requirement.

View file

@ -129,4 +129,4 @@ We are currently working on our own build tool called [Dioxus CLI](https://githu
The internal architecture of Dioxus was designed from day one to support the `LiveView` use-case, where a web server hosts a running app for each connected user. As of today, there is no first-class LiveView support you'll need to wire this up yourself.
While not currently fully implemented, the expectation is that LiveView apps can be a hybrid between Wasm and server-rendered where only portions of a page are "live" and the rest of the page is either server-rendered, statically generated, or handled by the host SPA.
While not currently fully implemented, the expectation is that LiveView apps can be a hybrid between Wasm and server-rendered where only portions of a page are "live" and the rest of the page is either server-rendered, statically generated, or handled by the host SPA.

View file

@ -0,0 +1,43 @@
# Sumário
[Introdução](index.md)
- [Introdução Rápida as Plataformas](getting_started/index.md)
- [Desktop](getting_started/desktop.md)
- [Web](getting_started/web.md)
- [Hot Reload](getting_started/hot_reload.md)
- [Renderização por Servidor](getting_started/ssr.md)
- [Interface do Terminal](getting_started/tui.md)
- [Móvel](getting_started/mobile.md)
- [Descrevendo a Interface do Usuário](describing_ui/index.md)
- [Atributos Especiais](describing_ui/special_attributes.md)
- [Componentes](describing_ui/components.md)
- [Props](describing_ui/component_props.md)
- [Componente Filho](describing_ui/component_children.md)
- [Interatividade](interactivity/index.md)
- [Manipuladores de Eventos](interactivity/event_handlers.md)
- [Hooks & Estado de Componentes](interactivity/hooks.md)
- [Entradas do Usuário](interactivity/user_input.md)
- [Estado Compartilhado](interactivity/sharing_state.md)
- [Hooks Personalizados](interactivity/custom_hooks.md)
- [Renderização Dinâmica](interactivity/dynamic_rendering.md)
- [Roteamento](interactivity/roteador.md)
- [Assincronia](async/index.md)
- [UseFuture](async/use_future.md)
- [UseCoroutine](async/use_coroutine.md)
- [Gerando Futures](async/spawn.md)
- [Práticas Recomendadas](best_practices/index.md)
- [Tratamento de erros](best_practices/error_handling.md)
- [Antipadrões](best_practices/antipatterns.md)
- [Publicação](publishing/index.md)
- [Desktop](publishing/desktop.md)
- [Web](publishing/web.md)
---
- [Renderizador Personalizado](custom_renderer/index.md)
---
[Roteiro](roadmap.md)
[Contribuindo](contributing.md)

View file

@ -0,0 +1,9 @@
# Trabalhando em Assincronia
Muitas vezes, os aplicativos precisam interagir com sistemas de arquivos, interfaces de rede, hardware ou temporizadores. Este capítulo fornece uma visão geral do uso de código assíncrono no Dioxus.
## O Tempo de Execução (runtime)
Por padrão, o Dioxus-Desktop vem com o runtime `Tokio` e configura tudo automaticamente para você. No momento, isso não é configurável, embora seja fácil escrever uma integração para o desktop Dioxus que use um tempo de execução assíncrono diferente.
Dioxus atualmente não é `thread-safe`, então qualquer código assíncrono que você escreve _não_ precisa ser `Send/Sync`. Isso significa que você pode usar estruturas não `thread-safe` como `Cell`, `Rc` e `RefCell`.

View file

@ -0,0 +1,29 @@
# Gerando Futures
Os **"hooks"** `use_future` e `use_coroutine` são úteis se você quiser gerar incondicionalmente o `Future`. Às vezes, porém, você desejará apenas gerar um `Future` em resposta a um evento, como um clique do mouse. Por exemplo, suponha que você precise enviar uma solicitação quando o usuário clicar em um botão "log in". Para isso, você pode usar `cx.spawn`:
```rust
{{#include ../../examples/spawn.rs:spawn}}
```
> Nota: `spawn` sempre gerará um _novo_ `Future`. Você provavelmente não quer chamá-lo em cada renderização.
O `Future` deve ser `'static` então quaisquer valores capturados pela tarefa não podem carregar nenhuma referência a `cx`, como um `UseState`.
No entanto, como você normalmente precisa de uma maneira de atualizar o valor de um gancho, você pode usar `to_owned` para criar um clone do _handle_ do _hook_. Você pode então usar esse clone no encerramento assíncrono.
Para tornar isso um pouco menos detalhado, o Dioxus exporta a macro `to_owned!` que criará uma ligação como mostrado acima, o que pode ser bastante útil ao lidar com muitos valores.
```rust
{{#include ../../examples/spawn.rs:to_owned_macro}}
```
Calling `spawn` will give you a `JoinHandle` which lets you cancel or pause the future.
## Gerando Tarefas do Tokio
Às vezes, você pode querer gerar uma tarefa em segundo plano que precise de vários _threads_ ou conversar com o hardware que pode bloquear o código do seu aplicativo. Nesses casos, podemos gerar diretamente uma tarefa Tokio do nosso `Future`. Para Dioxus-Desktop, sua tarefa será gerada no tempo de execução Multi-Tarefado do Tokio:
```rust
{{#include ../../examples/spawn.rs:tokio}}
```

View file

@ -0,0 +1,181 @@
# Corrotinas
Outra boa ferramenta para manter em sua caixa de ferramentas assíncrona são as corrotinas. Corrotinas são `Futures` que podem ser interrompidos, iniciados, pausados e retomados manualmente.
Assim como os `Futures` regulares, o código em uma corrotina Dioxus será executado até o próximo ponto `await` antes do _render_. Esse controle de baixo nível sobre tarefas assíncronas é bastante poderoso, permitindo tarefas em _loop_ infinito, como pesquisa de WebSocket, temporizadores em segundo plano e outras ações periódicas.
## `use_coroutine`
A configuração básica para corrotinas é o _hook_ `use_coroutine`. A maioria das corrotinas que escrevemos serão _loops_ de pesquisa usando `async`/`await`.
```rust
fn app(cx: Scope) -> Element {
let ws: &UseCoroutine<()> = use_coroutine(&cx, |rx| async move {
// Connect to some sort of service
let mut conn = connect_to_ws_server().await;
// Wait for data on the service
while let Some(msg) = conn.next().await {
// handle messages
}
});
}
```
Para muitos serviços, um _loop_ assíncrono simples lidará com a maioria dos casos de uso.
No entanto, se quisermos desabilitar temporariamente a corrotina, podemos "pausá-la" usando o método `pause` e "retomá-la" usando o método `resume`:
```rust
let sync: &UseCoroutine<()> = use_coroutine(&cx, |rx| async move {
// code for syncing
});
if sync.is_running() {
cx.render(rsx!{
button {
onclick: move |_| sync.pause(),
"Disable syncing"
}
})
} else {
cx.render(rsx!{
button {
onclick: move |_| sync.resume(),
"Enable syncing"
}
})
}
```
Esse padrão é onde as corrotinas são extremamente úteis em vez de escrever toda a lógica complicada para pausar nossas tarefas assíncronas como faríamos com `Promises` de JavaScript, o modelo do Rust nos permite simplesmente não pesquisar nosso `Future`.
## Enviando valores
Você deve ter notado que o encerramento `use_coroutine` recebe um argumento chamado `rx`. O que é aquilo? Bem, um padrão comum em aplicativos complexos é lidar com vários códigos assíncronos de uma só vez. Com bibliotecas como o Redux Toolkit, gerenciar várias promessas ao mesmo tempo pode ser um desafio e uma fonte comum de _bugs_.
Usando corrotinas, temos a oportunidade de centralizar nossa lógica assíncrona. O parâmetro `rx` é um canal ilimitado para código externo à corrotina para enviar dados _para_ a corrotina. Em vez de fazer um _loop_ em um serviço externo, podemos fazer um _loop_ no próprio canal, processando mensagens de dentro de nosso aplicativo sem precisar gerar um novo `Future`. Para enviar dados para a corrotina, chamaríamos "send" no _handle_.
```rust
enum ProfileUpdate {
SetUsername(String),
SetAge(i32)
}
let profile = use_coroutine(&cx, |mut rx: UnboundedReciver<ProfileUpdate>| async move {
let mut server = connect_to_server().await;
while let Ok(msg) = rx.next().await {
match msg {
ProfileUpdate::SetUsername(name) => server.update_username(name).await,
ProfileUpdate::SetAge(age) => server.update_age(age).await,
}
}
});
cx.render(rsx!{
button {
onclick: move |_| profile.send(ProfileUpdate::SetUsername("Bob".to_string())),
"Update username"
}
})
```
Para aplicativos suficientemente complexos, poderíamos criar vários "serviços" úteis diferentes que fazem um _loop_ nos canais para atualizar o aplicativo.
```rust
let profile = use_coroutine(&cx, profile_service);
let editor = use_coroutine(&cx, editor_service);
let sync = use_coroutine(&cx, sync_service);
async fn profile_service(rx: UnboundedReceiver<ProfileCommand>) {
// do stuff
}
async fn sync_service(rx: UnboundedReceiver<SyncCommand>) {
// do stuff
}
async fn editor_service(rx: UnboundedReceiver<EditorCommand>) {
// do stuff
}
```
Podemos combinar corrotinas com `Fermi` para emular o sistema `Thunk` do **Redux Toolkit** com muito menos dor de cabeça. Isso nos permite armazenar todo o estado do nosso aplicativo _dentro_ de uma tarefa e, em seguida, simplesmente atualizar os valores de "visualização" armazenados em `Atoms`. Não pode ser subestimado o quão poderosa é essa técnica: temos todas as vantagens das tarefas nativas do Rust com as otimizações e ergonomia do estado global. Isso significa que seu estado _real_ não precisa estar vinculado a um sistema como `Fermi` ou `Redux` os únicos `Atoms` que precisam existir são aqueles que são usados para controlar a interface.
```rust
static USERNAME: Atom<String> = |_| "default".to_string();
fn app(cx: Scope) -> Element {
let atoms = use_atom_root(&cx);
use_coroutine(&cx, |rx| sync_service(rx, atoms.clone()));
cx.render(rsx!{
Banner {}
})
}
fn Banner(cx: Scope) -> Element {
let username = use_read(&cx, USERNAME);
cx.render(rsx!{
h1 { "Welcome back, {username}" }
})
}
```
Agora, em nosso serviço de sincronização, podemos estruturar nosso estado como quisermos. Só precisamos atualizar os valores da _view_ quando estiver pronto.
```rust
enum SyncAction {
SetUsername(String),
}
async fn sync_service(mut rx: UnboundedReceiver<SyncAction>, atoms: AtomRoot) {
let username = atoms.write(USERNAME);
let errors = atoms.write(ERRORS);
while let Ok(msg) = rx.next().await {
match msg {
SyncAction::SetUsername(name) => {
if set_name_on_server(&name).await.is_ok() {
username.set(name);
} else {
errors.make_mut().push("SetUsernameFailed");
}
}
}
}
}
```
## Valores de Rendimento
Para obter valores de uma corrotina, basta usar um identificador `UseState` e definir o valor sempre que sua corrotina concluir seu trabalho.
```rust
let sync_status = use_state(&cx, || Status::Launching);
let sync_task = use_coroutine(&cx, |rx: UnboundedReceiver<SyncAction>| {
to_owned![sync_status];
async move {
loop {
delay_ms(1000).await;
sync_status.set(Status::Working);
}
}
})
```
## Injeção Automática na API de Contexto
Os identificadores de corrotina são injetados automaticamente por meio da API de contexto. `use_coroutine_handle` com o tipo de mensagem como genérico pode ser usado para buscar um _handle_.
```rust
fn Child(cx: Scope) -> Element {
let sync_task = use_coroutine_handle::<SyncAction>(&cx);
sync_task.send(SyncAction::SetUsername);
}
```

View file

@ -0,0 +1,31 @@
# `UseFuture`
[`use_future`](https://docs.rs/dioxus-hooks/latest/dioxus_hooks/fn.use_future.html) permite executar um encerramento assíncrono e fornece seu resultado.
Por exemplo, podemos fazer uma solicitação de API dentro de `use_future`:
```rust
{{#include ../../examples/use_future.rs:use_future}}
```
O código dentro de `use_future` será enviado ao agendador do Dioxus assim que o componente for renderizado.
Podemos usar `.value()` para obter o resultado do `Future`. Na primeira execução, como não há dados prontos quando o componente é carregado, seu valor será `None`. No entanto, uma vez finalizado o `Future`, o componente será renderizado novamente e o valor agora será `Some(...)`, contendo o valor de retorno do encerramento.
Podemos então renderizar esse resultado:
```rust
{{#include ../../examples/use_future.rs:render}}
```
## Reiniciando o `Future`
O identificador `UseFuture` fornece um método `restart`. Ele pode ser usado para executar o `Future` novamente, produzindo um novo valor.
## Dependências
Muitas vezes, você precisará executar o `Future` novamente toda vez que algum valor (por exemplo, uma prop) mudar. Ao invés de `.restart` manualmente, você pode fornecer uma tupla de "dependências" para o gancho. Ele executará automaticamente o `Future` quando qualquer uma dessas dependências for alterada. Exemplo:
```rust
{{#include ../../examples/use_future.rs:dependency}}
```

View file

@ -0,0 +1,33 @@
# Antipadrões
Este exemplo mostra o que não fazer e fornece uma razão pela qual um determinado padrão é considerado um "AntiPattern". A maioria dos antipadrões são considerados errados por motivos de desempenho ou por prejudicar a reutilização do código.
## Fragmentos Aninhados Desnecessariamente
Os fragmentos não montam um elemento físico no DOM imediatamente, então o Dioxus deve recorrer a seus filhos para encontrar um nó DOM físico. Este processo é chamado de "normalização". Isso significa que fragmentos profundamente aninhados fazem o Dioxus realizar um trabalho desnecessário. Prefira um ou dois níveis de fragmentos/componentes aninhados até apresentar um elemento DOM verdadeiro.
Apenas os nós Componente e Fragmento são suscetíveis a esse problema. O Dioxus atenua isso com componentes fornecendo uma API para registrar o estado compartilhado sem o padrão _Context Provider_.
```rust
{{#include ../../examples/anti_patterns.rs:nested_fragments}}
```
## Chaves do Iterador Incorretas
Conforme descrito no capítulo de renderização condicional, os itens da lista devem ter _keys_ exclusivas associadas aos mesmos itens nas renderizações. Isso ajuda o Dioxus a associar o estado aos componentes contidos e garante um bom desempenho de diferenciação. Não omita as _keys_, a menos que você saiba que a lista é estática e nunca será alterada.
```rust
{{#include ../../examples/anti_patterns.rs:iter_keys}}
```
## Evite Mutabilidade Interior em `Props`
Embora seja tecnicamente aceitável ter um `Mutex` ou um `RwLock` nos _props_, eles serão difíceis de usar.
Suponha que você tenha um _struct_ `User` contendo o campo `username: String`. Se você passar uma _prop_ `Mutex<User>` para um componente `UserComponent`, esse componente pode querer passar o nome de usuário como uma _prop_ `&str` para um componente filho. No entanto, ele não pode passar esse campo emprestado, pois ele só viveria enquanto o bloqueio do `Mutex`, que pertence à função `UserComponent`. Portanto, o componente será forçado a clonar o campo `username`.
## Evite Atualizar o Estado Durante a Renderização
Toda vez que você atualiza o estado, o Dioxus precisa renderizar novamente o componente isso é ineficiente! Considere refatorar seu código para evitar isso.
Além disso, se você atualizar incondicionalmente o estado durante a renderização, ele será renderizado novamente em um _loop_ infinito.

View file

@ -0,0 +1,153 @@
# Manipulação de Erros
Um ponto forte do Rust para desenvolvimento Web é a confiabilidade de sempre saber onde os erros podem ocorrer e ser forçado a lidar com eles
No entanto, não falamos sobre tratamento de erros neste guia! Neste capítulo, abordaremos algumas estratégias para lidar com erros para garantir que seu aplicativo nunca falhe.
## O mais simples retornando None
Observadores astutos podem ter notado que `Element` é na verdade um alias de tipo para `Option<VNode>`. Você não precisa saber o que é um `VNode`, mas é importante reconhecer que não poderíamos retornar nada:
```rust
fn App(cx: Scope) -> Element {
None
}
```
Isso nos permite adicionar um pouco de açúcar sintático para operações que achamos que _não devem_ falhar, mas ainda não estamos confiantes o suficiente para "desempacotar".
> A natureza de `Option<VNode>` pode mudar no futuro à medida que a característica `try` for atualizada.
```rust
fn App(cx: Scope) -> Element {
// immediately return "None"
let name = cx.use_hook(|_| Some("hi"))?;
}
```
## Retorno Antecipado do Resultado
Como o Rust não pode aceitar opções e resultados com a infraestrutura _try_ existente, você precisará manipular os resultados manualmente. Isso pode ser feito convertendo-os em `Option` ou manipulando-os explicitamente.
```rust
fn App(cx: Scope) -> Element {
// Convert Result to Option
let name = cx.use_hook(|_| "1.234").parse().ok()?;
// Early return
let count = cx.use_hook(|_| "1.234");
let val = match count.parse() {
Ok(val) => val
Err(err) => return cx.render(rsx!{ "Parsing failed" })
};
}
```
Observe que enquanto os ganchos no Dioxus não gostam de ser chamados em condicionais ou loops, eles _estão_ bem com retornos antecipados. Retornar um estado de erro antecipadamente é uma maneira completamente válida de lidar com erros.
## Resultados usando `match`
A próxima "melhor" maneira de lidar com erros no Dioxus é combinar (`match`) o erro localmente. Essa é a maneira mais robusta de lidar com erros, embora não seja dimensionada para arquiteturas além de um único componente.
Para fazer isso, simplesmente temos um estado de erro embutido em nosso componente:
```rust
let err = use_state(&cx, || None);
```
Sempre que realizarmos uma ação que gere um erro, definiremos esse estado de erro. Podemos então combinar o erro de várias maneiras (retorno antecipado, elemento de retorno etc.).
```rust
fn Commandline(cx: Scope) -> Element {
let error = use_state(&cx, || None);
cx.render(match *error {
Some(error) => rsx!(
h1 { "An error occured" }
)
None => rsx!(
input {
oninput: move |_| error.set(Some("bad thing happened!")),
}
)
})
}
```
## Passando Estados de Erro Através de Componentes
Se você estiver lidando com alguns componentes com um mínimo de aninhamento, basta passar o identificador de erro para componentes filhos.
```rust
fn Commandline(cx: Scope) -> Element {
let error = use_state(&cx, || None);
if let Some(error) = **error {
return cx.render(rsx!{ "An error occured" });
}
cx.render(rsx!{
Child { error: error.clone() }
Child { error: error.clone() }
Child { error: error.clone() }
Child { error: error.clone() }
})
}
```
Assim como antes, nossos componentes filhos podem definir manualmente o erro durante suas próprias ações. A vantagem desse padrão é que podemos isolar facilmente os estados de erro para alguns componentes por vez, tornando nosso aplicativo mais previsível e robusto.
## Tornando Global
Uma estratégia para lidar com erros em cascata em aplicativos maiores é sinalizar um erro usando o estado global. Esse padrão específico envolve a criação de um contexto de "erro" e, em seguida, defini-lo sempre que relevante. Este método em particular não é tão "sofisticado" quanto o controle de erros do React, mas é mais adequado para Rust.
Para começar, considere usar um _hook_ embutido como `use_context` e `use_context_provider` ou `Fermi`. Claro, é muito fácil criar seu próprio _hook_ também.
No "topo" de nossa arquitetura, queremos declarar explicitamente um valor que pode ser um erro.
```rust
enum InputError {
None,
TooLong,
TooShort,
}
static INPUT_ERROR: Atom<InputError> = |_| InputError::None;
```
Então, em nosso componente de nível superior, queremos tratar explicitamente o possível estado de erro para esta parte da árvore.
```rust
fn TopLevel(cx: Scope) -> Element {
let error = use_read(&cx, INPUT_ERROR);
match error {
TooLong => return cx.render(rsx!{ "FAILED: Too long!" }),
TooShort => return cx.render(rsx!{ "FAILED: Too Short!" }),
_ => {}
}
}
```
Agora, sempre que um componente _downstream_ tiver um erro em suas ações, ele pode simplesmente definir seu próprio estado de erro:
```rust
fn Commandline(cx: Scope) -> Element {
let set_error = use_set(&cx, INPUT_ERROR);
cx.render(rsx!{
input {
oninput: move |evt| {
if evt.value.len() > 20 {
set_error(InputError::TooLong);
}
}
}
})
}
```
Essa abordagem de tratamento de erros é melhor em aplicativos que têm estados de erro "bem definidos". Considere usar uma `crate` como `thiserror` ou `anyhow` para simplificar a geração dos tipos de erro.
Esse padrão é amplamente popular em muitos contextos e é particularmente útil sempre que seu código gera um erro irrecuperável. Você pode capturar esses estados de erro "globais" resultar em `panic!` ou estragar o estado.

View file

@ -0,0 +1,30 @@
# Práticas Recomendadas
## Componentes Reutilizáveis
Tanto quanto possível, divida seu código em pequenos componentes e _hooks_ reutilizáveis, em vez de implementar grandes partes da interface do usuário em um único componente. Isso ajudará você a manter o código sustentável é muito mais fácil, por exemplo, adicionar, remover ou reordenar partes da interface do usuário se ela estiver organizada em componentes.
Organize seus componentes em módulos para manter a base de código fácil de navegar!
## Minimize as Dependências do Estado
Embora seja possível compartilhar o estado entre os componentes, isso só deve ser feito quando necessário. Qualquer componente associado a um objeto de estado específico precisa ser renderizado novamente quando esse estado for alterado. Por esta razão:
- Mantenha o estado local para um componente, se possível
- Ao compartilhar o estado por meio de adereços, passe apenas os dados específicos necessários
## Bibliotecas Reutilizáveis
Ao publicar uma biblioteca projetada para funcionar com o Dioxus, é altamente recomendável usar apenas o recurso principal na `crate` `dioxus`. Isso faz com que sua `crate` seja compilada mais rapidamente, mais estável e evita a inclusão de bibliotecas incompatíveis que podem fazer com que ela não seja compilada em plataformas não suportadas.
❌ Não inclua dependências desnecessárias nas bibliotecas:
```toml
dioxus = { version = "...", features = ["web", "desktop", "full"]}
```
✅ Adicione apenas os recursos que você precisa:
```toml
dioxus = { version = "...", features = "core"}
```

View file

@ -0,0 +1,20 @@
# Contribuindo
O desenvolvimento acontece no [repositório do Dioxus no GitHub](https://github.com/DioxusLabs/dioxus). Se você encontrou um bug ou tem uma ideia para um recurso, envie um _issue_ (verifique se alguém ainda não o fez (https://github.com/DioxusLabs/dioxus/issues)).
[Discussões do GitHub](https://github.com/DioxusLabs/dioxus/discussions) podem ser usadas como um lugar para pedir ajuda ou falar sobre recursos. Você também pode participar do [nosso canal Discord](https://discord.gg/XgGxMSkvUM) onde algumas discussões de desenvolvimento acontecem.
## Como melhorar os documentos
Se você quiser melhorar os documentos, os PRs são bem-vindos! Ambos os documentos do Rust ([source](https://github.com/DioxusLabs/dioxus/tree/master/packages)) e este guia ([source](https://github.com/DioxusLabs/dioxus/tree/master /docs/guide)) pode ser encontrado no repositório do GitHub.
## Trabalhando no Ecossistema
Parte do que torna o React ótimo é o rico ecossistema. Gostaríamos do mesmo para Dioxus! Portanto, se você tem uma biblioteca em mente que gostaria de escrever e da qual acha que muitas pessoas se beneficiariam, ela será apreciada. Você pode [navegar no npm.js](https://www.npmjs.com/search?q=keywords:react-component) para se inspirar.
## Bugs e recursos
Se você corrigiu [um problema aberto](https://github.com/DioxusLabs/dioxus/issues), sinta-se à vontade para enviar um PR! Você também pode dar uma olhada no [roteiro](./roadmap.md) e trabalhar em algo lá. Considere [entre em contato](https://discord.gg/XgGxMSkvUM) com a equipe primeiro para garantir que todos estejam na mesma página e que você não faça um trabalho inútil!
Todas as solicitações de PR (incluindo aquelas feitas por um membro da equipe) devem ser aprovadas por pelo menos um outro membro da equipe.
Decisões maiores e mais sutis sobre design, arquitetura, mudanças de última hora, trade-offs, etc. são feitas por consenso da equipe.

View file

@ -0,0 +1,505 @@
# Renderizador Personalizado
Dioxus é uma estrutura incrivelmente portátil para desenvolvimento de interface do usuário. As lições, conhecimentos, _hooks_ e componentes que você adquire ao longo do tempo sempre podem ser usados para projetos futuros. No entanto, às vezes, esses projetos não podem aproveitar um renderizador compatível ou você precisa implementar seu próprio renderizador melhor.
Ótimas notícias: o design do renderizador depende inteiramente de você! Nós fornecemos sugestões e inspiração com os renderizadores originais, mas só realmente precisamos processar `DomEdits` e enviar `UserEvents`.
## Detalhes
A implementação do renderizador é bastante simples. O renderizador precisa:
1. Lidar com o fluxo de edições gerado por atualizações no DOM virtual
2. Registrar ouvintes e passar eventos para o sistema de eventos do DOM virtual
Essencialmente, seu renderizador precisa implementar a `trait` `RealDom` e gerar objetos `EventTrigger` para atualizar o `VirtualDOM`. A partir daí, você terá tudo o que precisa para renderizar o `VirtualDOM` na tela.
Internamente, o Dioxus lida com o relacionamento da árvore, `diffing`, gerenciamento de memória e o sistema de eventos, deixando o mínimo necessário para que os renderizadores se implementem.
Como referência, confira o interpretador `javascript` ou o renderizador `tui` como ponto de partida para seu renderizador personalizado.
## DomEdit
O tipo "DomEdit" é uma `enum` serializada que representa uma operação atômica que ocorre no `RealDom`. As variantes seguem aproximadamente este conjunto:
```rust
enum DomEdit {
PushRoot,
AppendChildren,
ReplaceWith,
InsertAfter,
InsertBefore,
Remove,
CreateTextNode,
CreateElement,
CreateElementNs,
CreatePlaceholder,
NewEventListener,
RemoveEventListener,
SetText,
SetAttribute,
RemoveAttribute,
PopRoot,
}
```
O mecanismo de diferenciação Dioxus opera como uma [máquina de pilha] (https://en.wikipedia.org/wiki/Stack_machine) onde o método "push_root" empurra um novo nó DOM "real" para a pilha e "append_child" e "replace_with" " ambos removem nós da pilha.
### Exemplo
Para fins de compreensão, vamos considerar este exemplo uma declaração de interface do usuário muito simples:
```rust
rsx!( h1 {"hello world"} )
```
Para começar, o Dioxus deve primeiro navegar até o contêiner dessa tag h1. Para "navegar" aqui, o algoritmo de diferenciação interna gera o `DomEdit` `PushRoot` onde o ID da raiz é o contêiner.
Quando o renderizador recebe essa instrução, ele empurra o `Node` real para sua própria pilha. A pilha do renderizador real ficará assim:
```rust
instructions: [
PushRoot(Container)
]
stack: [
ContainerNode,
]
```
Em seguida, o Dioxus encontrará o nó `h1`. O algoritmo `diff` decide que este nó precisa ser criado, então o Dioxus irá gerar o DomEdit `CreateElement`. Quando o renderizador receber esta instrução, ele criará um nó desmontado e o enviará para sua própria pilha (_stack_):
```rust
instructions: [
PushRoot(Container),
CreateElement(h1),
]
stack: [
ContainerNode,
h1,
]
```
Em seguida, Dioxus vê o nó de texto e gera o DomEdit `CreateTextNode`:
```rust
instructions: [
PushRoot(Container),
CreateElement(h1),
CreateTextNode("hello world")
]
stack: [
ContainerNode,
h1,
"hello world"
]
```
Lembre-se, o nó de texto não está anexado a nada (ele está desmontado), então o Dioxus precisa gerar um _Edit_ que conecte o nó de texto ao elemento `h1`. Depende da situação, mas neste caso usamos `AppendChildren`. Isso remove o nó de texto da _stack_, deixando o elemento `h1` como o próximo elemento na linha.
```rust
instructions: [
PushRoot(Container),
CreateElement(h1),
CreateTextNode("hello world"),
AppendChildren(1)
]
stack: [
ContainerNode,
h1
]
```
Chamamos `AppendChildren` novamente, retirando o nó `h1` e anexando-o ao pai:
```rust
instructions: [
PushRoot(Container),
CreateElement(h1),
CreateTextNode("hello world"),
AppendChildren(1),
AppendChildren(1)
]
stack: [
ContainerNode,
]
```
Finalmente, o contêiner é aberto, pois não precisamos mais dele.
```rust
instructions: [
PushRoot(Container),
CreateElement(h1),
CreateTextNode("hello world"),
AppendChildren(1),
AppendChildren(1),
PopRoot
]
stack: []
```
Com o tempo, nossa _stack_ ficou assim:
```rust
[]
[Container]
[Container, h1]
[Container, h1, "hello world"]
[Container, h1]
[Container]
[]
```
Observe como nossa _stack_ fica vazia depois que a interface do usuário é montada. Convenientemente, essa abordagem separa completamente o `VirtualDOM` e o `RealDOM`. Além disso, essas edições são serializáveis, o que significa que podemos até gerenciar UIs em uma conexão de rede. Esta pequena _stack_ e edições serializadas tornam o Dioxus independente das especificidades da plataforma.
Dioxus também é muito rápido. Como o Dioxus divide a fase de `diff` e `patch`, ele é capaz de fazer todas as edições no `RealDOM` em um período de tempo muito curto (menos de um único quadro), tornando a renderização muito rápida. Ele também permite que o Dioxus cancele grandes operações de diferenciação se ocorrer um trabalho de prioridade mais alta durante a diferenciação.
É importante notar que há uma camada de conexão entre o Dioxus e o renderizador. Dioxus salva e carrega elementos (a edição `PushRoot`) com um ID. Dentro do `VirtualDOM`, isso é rastreado apenas como um `u64`.
Sempre que uma edição `CreateElement` é gerada durante a comparação, o Dioxus incrementa seu contador de nós e atribui a esse novo elemento seu `NodeCount` atual. O `RealDom` é responsável por lembrar este ID e enviar o nó correto quando `PushRoot(ID)` é gerado. Dioxus recupera IDs de elementos quando removidos. Para ficar em sincronia com Dioxus, você pode usar um `Sparce Vec` (`Vec<Option<T>>`) com itens possivelmente desocupados. Você pode usar os ids como índices no `Vec` para elementos e aumentar o `Vec` quando um id não existir.
Esta pequena demonstração serve para mostrar exatamente como um Renderer precisaria processar um stream de edição para construir UIs. Um conjunto de `DomEdits` serializados para várias demos está disponível para você testar seu renderizador personalizado.
## Ciclo de Eventos
Como a maioria das GUIs, o Dioxus conta com um _loop_ de eventos para progredir no `VirtualDOM`. O próprio `VirtualDOM` também pode produzir eventos, por isso é importante que seu renderizador personalizado também possa lidar com eles.
O código para a implementação do `WebSys` é direto, então vamos adicioná-lo aqui para demonstrar como um `loop` de eventos é simples:
```rust
pub async fn run(&mut self) -> dioxus_core::error::Result<()> {
// Push the body element onto the WebsysDom's stack machine
let mut websys_dom = crate::new::WebsysDom::new(prepare_websys_dom());
websys_dom.stack.push(root_node);
// Rebuild or hydrate the virtualdom
let mutations = self.internal_dom.rebuild();
websys_dom.apply_mutations(mutations);
// Wait for updates from the real dom and progress the virtual dom
loop {
let user_input_future = websys_dom.wait_for_event();
let internal_event_future = self.internal_dom.wait_for_work();
match select(user_input_future, internal_event_future).await {
Either::Left((_, _)) => {
let mutations = self.internal_dom.work_with_deadline(|| false);
websys_dom.apply_mutations(mutations);
},
Either::Right((event, _)) => websys_dom.handle_event(event),
}
// render
}
}
```
É importante que você decodifique os eventos reais do seu sistema de eventos no sistema de eventos sintético do Dioxus (entenda sintético como abstraído). Isso significa simplesmente combinar seu tipo de evento e criar um tipo Dioxus `UserEvent`. No momento, o sistema `VirtualEvent` é modelado quase inteiramente em torno da especificação HTML, mas estamos interessados em reduzi-lo.
```rust
fn virtual_event_from_websys_event(event: &web_sys::Event) -> VirtualEvent {
match event.type_().as_str() {
"keydown" => {
let event: web_sys::KeyboardEvent = event.clone().dyn_into().unwrap();
UserEvent::KeyboardEvent(UserEvent {
scope_id: None,
priority: EventPriority::Medium,
name: "keydown",
// This should be whatever element is focused
element: Some(ElementId(0)),
data: Arc::new(KeyboardData{
char_code: event.char_code(),
key: event.key(),
key_code: event.key_code(),
alt_key: event.alt_key(),
ctrl_key: event.ctrl_key(),
meta_key: event.meta_key(),
shift_key: event.shift_key(),
location: event.location(),
repeat: event.repeat(),
which: event.which(),
})
})
}
_ => todo!()
}
}
```
## Elementos brutos personalizados
Se você precisa ir tão longe a ponto de confiar em elementos personalizados para o seu renderizador você pode. Isso ainda permite que você use a natureza reativa do Dioxus, sistema de componentes, estado compartilhado e outros recursos, mas acabará gerando nós diferentes. Todos os atributos e ouvintes para o namespace HTML e SVG são transportados por meio de estruturas auxiliares que essencialmente compilam (não representam sobrecarga de tempo de execução). Você pode colocar seus próprios elementos a qualquer hora, sem problemas. No entanto, você deve ter certeza absoluta de que seu renderizador pode lidar com o novo tipo, ou ele irá "bater e queimar".
Esses elementos personalizados são definidos como estruturas de unidade com implementações de características.
Por exemplo, o elemento `div` é (aproximadamente!) definido assim:
```rust
struct div;
impl div {
/// Some glorious documentation about the class property.
const TAG_NAME: &'static str = "div";
const NAME_SPACE: Option<&'static str> = None;
// define the class attribute
pub fn class<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
cx.attr("class", val, None, false)
}
// more attributes
}
```
Você provavelmente notou que muitos elementos nas macros `rsx!` suportam documentação em foco. A abordagem que adotamos para elementos personalizados significa que a estrutura da unidade é criada imediatamente onde o elemento é usado na macro. Quando a macro é expandida, os comentários doc ainda se aplicam à estrutura da unidade, dando toneladas de feedback no editor, mesmo dentro de uma `macro proc`.
# Núcleo Nativo
Se você estiver criando um renderizador em Rust, o núcleo nativo fornece alguns utilitários para implementar um renderizador. Ele fornece uma abstração sobre `DomEdits` e manipula o layout para você.
## RealDom
O `RealDom` é uma abstração de nível superior sobre a atualização do Dom. Ele atualiza com `DomEdits` e fornece uma maneira de atualizar incrementalmente o estado dos nós com base em quais atributos mudam.
### Exemplo
Vamos construir um renderizador de exemplo com bordas, tamanho e cor do texto.
Antes de começarmos, vamos dar uma olhada em um elemento de exemplo que podemos renderizar:
```rust
cx.render(rsx!{
div{
color: "red",
p{
border: "1px solid black",
"hello world"
}
}
})
```
Nesta árvore a cor depende da cor do pai. O tamanho depende do tamanho das _children_, do texto atual e do tamanho do texto. A borda depende apenas do nó atual.
No diagrama a seguir, as setas representam o fluxo de dados:
[![](https://mermaid.ink/img/pako:eNqdVNFqgzAU_RXJXizUUZPJmIM-jO0LukdhpCbO0JhIGteW0n9fNK1Oa0brfUnu9VxyzzkXjyCVhIIYZFzu0hwr7X2-JcIzsa3W3wqXuZdKoele22oddfa1Y0Tnfn31muvMfqeCDNq3GmvaNROmaKqZFO1DPTRhP8MOd1fTWYNDvzlmQbBMJZcq9JtjNgY1mLVUhBqQPQeojl3wGCw5PsjqnIe-zXqEL8GZ2Kz0gVMPmoeU3ND4IcuiaLGY2zRouuKncv_qGKv3VodpJe0JVU6QCQ5kgqMyWQVr8hbk4hm1PBcmsuwmnrCVH94rP7xN_ucp8sOB_EPSfz9drYVrkpc_AmH8_yTjJueUc-ntpOJkgt2os9tKjcYlt-DLUiD3UsB2KZCLcwjv3Aq33-g2v0M0xXA0MBy5DUdXi-gcJZriuLmAOSioKjAj5ld8rMsJ0DktaAJicyVYbRKQiJPBVSUx438QpqUCcYb5ls4BrrRcHUTaFizqnWGzR8W5evoFI-bJdw)](https://mermaid-js.github.io/mermaid-live-editor/edit#pako:eNqdVNFqgzAU_RXJXizUUZPJmIM-jO0LukdhpCbO0JhIGteW0n9fNK1Oa0brfUnu9VxyzzkXjyCVhIIYZFzu0hwr7X2-JcIzsa3W3wqXuZdKoele22oddfa1Y0Tnfn31muvMfqeCDNq3GmvaNROmaKqZFO1DPTRhP8MOd1fTWYNDvzlmQbBMJZcq9JtjNgY1mLVUhBqQPQeojl3wGCw5PsjqnIe-zXqEL8GZ2Kz0gVMPmoeU3ND4IcuiaLGY2zRouuKncv_qGKv3VodpJe0JVU6QCQ5kgqMyWQVr8hbk4hm1PBcmsuwmnrCVH94rP7xN_ucp8sOB_EPSfz9drYVrkpc_AmH8_yTjJueUc-ntpOJkgt2os9tKjcYlt-DLUiD3UsB2KZCLcwjv3Aq33-g2v0M0xXA0MBy5DUdXi-gcJZriuLmAOSioKjAj5ld8rMsJ0DktaAJicyVYbRKQiJPBVSUx438QpqUCcYb5ls4BrrRcHUTaFizqnWGzR8W5evoFI-bJdw)
[//]: # '%% mermaid flow chart'
[//]: # 'flowchart TB'
[//]: # ' subgraph context'
[//]: # ' text_width(text width)'
[//]: # ' end'
[//]: # ' subgraph state'
[//]: # ' direction TB'
[//]: # ' subgraph div state'
[//]: # ' direction TB'
[//]: # ' state1(state)-->color1(color)'
[//]: # ' state1-->border1(border)'
[//]: # ' text_width-.->layout_width1(layout width)'
[//]: # ' linkStyle 2 stroke:#ff5500,stroke-width:4px;'
[//]: # ' state1-->layout_width1'
[//]: # ' end'
[//]: # ' subgraph p state'
[//]: # ' direction TB'
[//]: # ' state2(state)-->color2(color)'
[//]: # ' color1-.->color2'
[//]: # ' linkStyle 5 stroke:#0000ff,stroke-width:4px;'
[//]: # ' state2-->border2(border)'
[//]: # ' text_width-.->layout_width2(layout width)'
[//]: # ' linkStyle 7 stroke:#ff5500,stroke-width:4px;'
[//]: # ' state2-->layout_width2'
[//]: # ' layout_width2-.->layout_width1'
[//]: # ' linkStyle 9 stroke:#00aa00,stroke-width:4px;'
[//]: # ' end'
[//]: # ' subgraph hello world state'
[//]: # ' direction TB'
[//]: # ' state3(state)-->border3(border)'
[//]: # ' state3-->color3(color)'
[//]: # ' color2-.->color3'
[//]: # ' linkStyle 12 stroke:#0000ff,stroke-width:4px;'
[//]: # ' text_width-.->layout_width3(layout width)'
[//]: # ' linkStyle 13 stroke:#ff5500,stroke-width:4px;'
[//]: # ' state3-->layout_width3'
[//]: # ' layout_width3-.->layout_width2'
[//]: # ' linkStyle 15 stroke:#00aa00,stroke-width:4px;'
[//]: # ' end'
[//]: # ' end'
Para ajudar na construção de um DOM, o núcleo nativo fornece quatro `traits`: `State`, `ChildDepState`, `ParentDepState` e `NodeDepState` e uma estrutura `RealDom`. O `ChildDepState`, `ParentDepState` e `NodeDepState` fornecem uma maneira de descrever como algumas informações em um nó se relacionam com as de seus parentes. Ao fornecer como construir um único nó a partir de suas relações, o native-core derivará uma maneira de atualizar o estado de todos os nós para você com `#[derive(State)]`. Depois de ter um estado, você pode fornecê-lo como genérico ao `RealDom`. `RealDom` fornece todos os métodos para interagir e atualizar seu novo dom.
```rust
use dioxus_native_core::node_ref::*;
use dioxus_native_core::state::{ChildDepState, NodeDepState, ParentDepState, State};
use dioxus_native_core_macro::{sorted_str_slice, State};
#[derive(Default, Copy, Clone)]
struct Size(f32, f32);
// Size only depends on the current node and its children, so it implements ChildDepState
impl ChildDepState for Size {
// Size accepts a font size context
type Ctx = f32;
// Size depends on the Size part of each child
type DepState = Self;
// Size only cares about the width, height, and text parts of the current node
const NODE_MASK: NodeMask =
NodeMask::new_with_attrs(AttributeMask::Static(&sorted_str_slice!(["width", "height"]))).with_text();
fn reduce<'a>(
&mut self,
node: NodeView,
children: impl Iterator<Item = &'a Self::DepState>,
ctx: &Self::Ctx,
) -> bool
where
Self::DepState: 'a,
{
let mut width;
let mut height;
if let Some(text) = node.text() {
// if the node has text, use the text to size our object
width = text.len() as f32 * ctx;
height = ctx;
} else {
// otherwise, the size is the maximum size of the children
width = *children
.reduce(|accum, item| if accum >= item.0 { accum } else { item.0 })
.unwrap_or(0.0));
height = *children
.reduce(|accum, item| if accum >= item.1 { accum } else { item.1 })
.unwrap_or(&0.0);
}
// if the node contains a width or height attribute it overrides the other size
for a in node.attibutes(){
match a.name{
"width" => width = a.value.parse().unwrap(),
"height" => height = a.value.parse().unwrap(),
// because Size only depends on the width and height, no other attributes will be passed to the member
_ => panic!()
}
}
// to determine what other parts of the dom need to be updated we return a boolean that marks if this member changed
let changed = (width != self.0) || (height != self.1);
*self = Self(width, height);
changed
}
}
#[derive(Debug, Clone, Copy, PartialEq, Default)]
struct TextColor {
r: u8,
g: u8,
b: u8,
}
// TextColor only depends on the current node and its parent, so it implements ParentDepState
impl ParentDepState for TextColor {
type Ctx = ();
// TextColor depends on the TextColor part of the parent
type DepState = Self;
// TextColor only cares about the color attribute of the current node
const NODE_MASK: NodeMask = NodeMask::new_with_attrs(AttributeMask::Static(&["color"]));
fn reduce(
&mut self,
node: NodeView,
parent: Option<&Self::DepState>,
_ctx: &Self::Ctx,
) -> bool {
// TextColor only depends on the color tag, so getting the first tag is equivilent to looking through all tags
let new = match node.attributes().next() {
// if there is a color tag, translate it
Some("red") => TextColor { r: 255, g: 0, b: 0 },
Some("green") => TextColor { r: 0, g: 255, b: 0 },
Some("blue") => TextColor { r: 0, g: 0, b: 255 },
Some(_) => panic!("unknown color"),
// otherwise check if the node has a parent and inherit that color
None => match parent {
Some(parent) => *parent,
None => Self::default(),
},
};
// check if the member has changed
let changed = new != *self;
*self = new;
changed
}
}
#[derive(Debug, Clone, PartialEq, Default)]
struct Border(bool);
// TextColor only depends on the current node, so it implements NodeDepState
impl NodeDepState for Border {
type Ctx = ();
// Border does not depended on any other member in the current node
type DepState = ();
// Border does not depended on any other member in the current node
const NODE_MASK: NodeMask =
NodeMask::new_with_attrs(AttributeMask::Static(&["border"]));
fn reduce(&mut self, node: NodeView, _sibling: &Self::DepState, _ctx: &Self::Ctx) -> bool {
// check if the node contians a border attribute
let new = Self(node.attributes().next().map(|a| a.name == "border").is_some());
// check if the member has changed
let changed = new != *self;
*self = new;
changed
}
}
// State provides a derive macro, but anotations on the members are needed in the form #[dep_type(dep_member, CtxType)]
#[derive(State, Default, Clone)]
struct ToyState {
// the color member of it's parent and no context
#[parent_dep_state(color)]
color: TextColor,
// depends on the node, and no context
#[node_dep_state()]
border: Border,
// depends on the layout_width member of children and f32 context (for text size)
#[child_dep_state(size, f32)]
size: Size,
}
```
Agora que temos nosso estado, podemos colocá-lo em uso em nosso DOM. Você pode atualizar o DOM com `update_state` para atualizar a estrutura do dom (adicionando, removendo e alterando as propriedades dos nós) e então `apply_mutations` para atualizar o `ToyState` para cada um dos nós que foram alterados.
```rust
fn main(){
fn app(cx: Scope) -> Element {
cx.render(rsx!{
div{
color: "red",
"hello world"
}
})
}
let vdom = VirtualDom::new(app);
let rdom: RealDom<ToyState> = RealDom::new();
let mutations = dom.rebuild();
// update the structure of the real_dom tree
let to_update = rdom.apply_mutations(vec![mutations]);
let mut ctx = AnyMap::new();
// set the font size to 3.3
ctx.insert(3.3);
// update the ToyState for nodes in the real_dom tree
let _to_rerender = rdom.update_state(&dom, to_update, ctx).unwrap();
// we need to run the vdom in a async runtime
tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()?
.block_on(async {
loop{
let wait = vdom.wait_for_work();
let mutations = vdom.work_with_deadline(|| false);
let to_update = rdom.apply_mutations(mutations);
let mut ctx = AnyMap::new();
ctx.insert(3.3);
let _to_rerender = rdom.update_state(vdom, to_update, ctx).unwrap();
// render...
}
})
}
```
## Layout
Para a maioria das plataformas, o _layout_ dos `Elementos` permanecerá o mesmo. O módulo `layout_attributes` fornece uma maneira de aplicar atributos html a um estilo de _layout_ estendido.
## Conclusão
Pronto! Você deve ter quase todo o conhecimento necessário sobre como implementar seu próprio renderizador. Estamos super interessados em ver os aplicativos Dioxus trazidos para renderizadores de desktop personalizados, renderizador para dispositivos móveis, interface do usuário de videogame e até realidade aumentada! Se você estiver interessado em contribuir para qualquer um desses projetos, não tenha medo de entrar em contato ou se juntar à comunidade.

View file

@ -0,0 +1,31 @@
# Componente Filhos
Em alguns casos, você pode desejar criar um componente que atue como um contêiner para algum outro conteúdo, sem que o componente precise saber qual é esse conteúdo. Para conseguir isso, crie uma _prop_ do tipo `Element`:
```rust
{{#include ../../examples/component_element_props.rs:Clickable}}
```
Então, ao renderizar o componente, você pode passar a saída de `cx.render(rsx!(...))`:
```rust
{{#include ../../examples/component_element_props.rs:Clickable_usage}}
```
> Nota: Como `Element<'a>` é uma _prop_ emprestado, não haverá memoização.
> Atenção: Embora possa compilar, não inclua o mesmo `Element` mais de uma vez no RSX. O comportamento resultante não é especificado.
## O Campo `children`
Em vez de passar o `RSX` através de uma _prop_ regular, você pode querer aceitar filhos da mesma forma que os elementos podem ter filhos. O prop "mágico" `children` permite que você consiga isso:
```rust
{{#include ../../examples/component_children.rs:Clickable}}
```
Isso torna o uso do componente muito mais simples: basta colocar o `RSX` dentro dos colchetes `{}` e não há necessidade de uma chamada `render` ou outra macro!
```rust
{{#include ../../examples/component_children.rs:Clickable_usage}}
```

View file

@ -0,0 +1,142 @@
# Props de Componentes
Assim como você pode passar argumentos para uma função, você pode passar _props_ para um componente que personaliza seu comportamento! Os componentes que vimos até agora não aceitam _props_ então vamos escrever alguns componentes que aceitam.
## `#[derive(Props)]`
_Props_ de componente são uma única estrutura anotada com `#[derive(Props)]`. Para um componente aceitar _props_, o tipo de seu argumento deve ser `Scope<YourPropsStruct>`. Então, você pode acessar o valor das _props_ usando `cx.props`.
Existem 2 tipos de estruturas Props:
- `props` próprios:
- Não tem uma vida útil associada
- Implementam `PartialEq`, permitindo a memoização (se os _props_ não mudarem, o Dioxus não renderizará novamente o componente)
- `props` emprestados:
- [Emprestado](https://doc.rust-lang.org/beta/rust-by-example/scope/borrow.html) de um componente pai
- Não pode ser memoizado devido a restrições de tempo de vida (Rust's lifetime)
### Props Próprios
_Props_ próprios são muito simples eles não emprestam nada. Exemplo:
```rust
{{#include ../../examples/component_owned_props.rs:Likes}}
```
Você pode então passar valores de _prop_ para o componente da mesma forma que você passaria atributos para um elemento:
```rust
{{#include ../../examples/component_owned_props.rs:App}}
```
![Screenshot: Likes component](./images/component_owned_props_screenshot.png)
### Props Emprestados
Possuir _props_ funciona bem se seus _props_ forem fáceis de copiar como um único número. Mas e se precisarmos passar um tipo de dados maior, como uma `String` de um componente `App` para um subcomponente `TitleCard`? Uma solução ingênua pode ser [`.clone()`](https://doc.rust-lang.org/std/clone/trait.Clone.html) a `String`, criando uma cópia dela para o subcomponente mas isso seria ineficiente, especialmente para `Strings` maiores.
Rust permite algo mais eficiente emprestar a `String` como um `&str` é para isso que servem as _props emprestadas_!
```rust
{{#include ../../examples/component_borrowed_props.rs:TitleCard}}
```
Podemos então usar o componente assim:
```rust
{{#include ../../examples/component_borrowed_props.rs:App}}
```
![Screenshot: TitleCard component](./images/component_borrowed_props_screenshot.png)
## Props de Option
A macro `#[derive(Props)]` tem alguns recursos que permitem personalizar o comportamento dos adereços.
### Props Opcionais
Você pode criar campos opcionais usando o tipo `Option<…>` para um campo:
```rust
{{#include ../../examples/component_props_options.rs:OptionalProps}}
```
Em seguida, você pode optar por fornecê-los ou não:
```rust
{{#include ../../examples/component_props_options.rs:OptionalProps_usage}}
```
### `Option` Explicitamente Obrigatórias
Se você quiser exigir explicitamente uma `Option`, e não uma _prop_ opcional, você pode anotá-la com `#[props(!optional)]`:
```rust
{{#include ../../examples/component_props_options.rs:ExplicitOption}}
```
Então, você tem que passar explicitamente `Some("str")` ou `None`:
```rust
{{#include ../../examples/component_props_options.rs:ExplicitOption_usage}}
```
### Props Padrão
Você pode usar `#[props(default = 42)]` para tornar um campo opcional e especificar seu valor padrão:
```rust
{{#include ../../examples/component_props_options.rs:DefaultComponent}}
```
Então, da mesma forma que _props_ opcionais, você não precisa fornecê-lo:
```rust
{{#include ../../examples/component_props_options.rs:DefaultComponent_usage}}
```
### Conversão Automática com `.into`
É comum que as funções Rust aceitem `impl Into<SomeType>` em vez de apenas `SomeType` para suportar uma ampla gama de parâmetros. Se você quiser uma funcionalidade semelhante com _props_, você pode usar `#[props(into)]`. Por exemplo, você pode adicioná-lo em uma prop `String` e `&str` também será aceito automaticamente, pois pode ser convertido em `String`:
```rust
{{#include ../../examples/component_props_options.rs:IntoComponent}}
```
Então, você pode usá-lo assim:
```rust
{{#include ../../examples/component_props_options.rs:IntoComponent_usage}}
```
## A macro `inline_props`
Até agora, todas as funções `Component` que vimos tinham uma _struct_ `ComponentProps` correspondente para passar em _props_. Isso foi bastante verboso... Não seria legal ter _props_ como argumentos de função simples? Então não precisaríamos definir uma estrutura Props, e ao invés de digitar `cx.props.whatever`, poderíamos usar `whatever` diretamente!
`inline_props` permite que você faça exatamente isso. Em vez de digitar a versão "completa":
```rust
#[derive(Props, PartialEq)]
struct TitleCardProps {
title: String,
}
fn TitleCard(cx: Scope<TitleCardProps>) -> Element {
cx.render(rsx!{
h1 { "{cx.props.title}" }
})
}
```
...você pode definir uma função que aceita _props_ como argumentos. Então, basta anotá-lo com `#[inline_props]`, e a macro irá transformá-lo em um componente regular para você:
```rust
#[inline_props]
fn TitleCard(cx: Scope, title: String) -> Element {
cx.render(rsx!{
h1 { "{title}" }
})
}
```
> Embora o novo Componente seja mais curto e fácil de ler, essa macro não deve ser usada por autores de bibliotecas, pois você tem menos controle sobre a documentação do Prop.

View file

@ -0,0 +1,27 @@
# Componentes
Assim como você não gostaria de escrever um programa complexo em uma única e longa função `main`, você não deve construir uma interface complexa em uma única função `App`. Em vez disso, seria melhor dividir a funcionalidade de um aplicativo em partes lógicas chamadas componentes.
Um componente é uma função Rust, nomeada em _UpperCammelCase_, que recebe um parâmetro `Scope` e retorna um `Element` descrevendo a interface do usuário que deseja renderizar. Na verdade, nossa função `App` é um componente!
```rust
{{#include ../../examples/hello_world_desktop.rs:component}}
```
> Você provavelmente desejará adicionar `#![allow(non_snake_case)]` no topo de sua caixa para evitar avisos sobre o nome da função
Um Componente é responsável por alguma tarefa de renderização normalmente, renderizando uma parte isolada da interface do usuário. Por exemplo, você pode ter um componente `About` que renderiza uma breve descrição do Dioxus Labs:
```rust
{{#include ../../examples/components.rs:About}}
```
Em seguida, você pode renderizar seu componente em outro componente, da mesma forma que os elementos são renderizados:
```rust
{{#include ../../examples/components.rs:App}}
```
![Captura de tela contendo o componente Sobre duas vezes](./images/screenshot_about_component.png)
> Neste ponto, pode parecer que os componentes nada mais são do que funções. No entanto, à medida que você aprende mais sobre os recursos do Dioxus, verá que eles são realmente mais poderosos!

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View file

@ -0,0 +1,116 @@
# Descrevendo a Interface do Usuário
Dioxus é uma estrutura _declarativa_. Isso significa que, em vez de dizer ao Dioxus o que fazer (por exemplo, "criar um elemento" ou "definir a cor para vermelho"), simplesmente _declaramos_ como queremos que a interface do usuário se pareça usando o RSX.
Você já viu um exemplo simples ou sintaxe `RSX` no aplicativo "hello world":
```rust
{{#include ../../examples/hello_world_desktop.rs:component}}
```
Aqui, usamos a macro `rsx!` para _declarar_ que queremos um elemento `div`, contendo o texto `"Hello, world!"`. Dioxus pega o RSX e constrói uma interface do usuário a partir dele.
## Recursos do RSX
O RSX é muito semelhante ao HTML, pois descreve elementos com atributos e filhos. Aqui está um elemento `div` vazio no RSX, bem como o HTML resultante:
```rust
{{#include ../../examples/rsx_overview.rs:empty}}
```
```html
<div></div>
```
### Filhos
Para adicionar filhos a um elemento, coloque-os dentro dos colchetes `{}`. Eles podem ser outros elementos ou texto. Por exemplo, você pode ter um elemento `ol` (lista ordenada), contendo 3 elementos `li` (item da lista), cada um dos quais contém algum texto:
```rust
{{#include ../../examples/rsx_overview.rs:children}}
```
```html
<ol>
<li>First Item</li>
<li>Second Item</li>
<li>Third Item</li>
</ol>
```
### Fragmentos
Você também pode "agrupar" elementos envolvendo-os em `Fragment {}`. Isso não criará nenhum elemento adicional.
> Nota: você também pode renderizar vários elementos no nível superior de `rsx!` e eles serão agrupados automaticamente não há necessidade de um `Fragment {}` explícito lá.
```rust
{{#include ../../examples/rsx_overview.rs:fragments}}
```
```html
<p>First Item</p>
<p>Second Item</p>
<span>a group</span>
<span>of three</span>
<span>items</span>
```
### Atributos
Os atributos também são especificados dentro dos colchetes `{}`, usando a sintaxe `name: value`. Você pode fornecer o valor como um literal no RSX:
```rust
{{#include ../../examples/rsx_overview.rs:attributes}}
```
```html
<a
href="https://www.youtube.com/watch?v=dQw4w9WgXcQ"
class="primary_button"
autofocus="true"
>Log In</a
>
```
> Nota: Todos os atributos definidos em `dioxus-html` seguem a convenção de nomenclatura snake_case. Eles transformam seus nomes `snake_case` em atributos `camelCase` do HTML.
#### Atributos Personalizados
Dioxus tem um conjunto pré-configurado de atributos que você pode usar. O RSX é validado em tempo de compilação para garantir que você não especificou um atributo inválido. Se você quiser substituir esse comportamento por um nome de atributo personalizado, especifique o atributo entre aspas:
```rust
{{#include ../../examples/rsx_overview.rs:custom_attributes}}
```
```html
<b customAttribute="value"> Rust is cool </b>
```
### Interpolação
Da mesma forma que você pode [formatar](https://doc.rust-lang.org/rust-by-example/hello/print/fmt.html) Rust _strings_, você também pode interpolar no texto RSX. Use `{variable}` para exibir o valor de uma variável em uma _string_, ou `{variable:?}` para usar a representação `Debug`:
```rust
{{#include ../../examples/rsx_overview.rs:formatting}}
```
```html
<div class="country-es">
Coordinates: (42, 0)
<div>ES</div>
<div>42</div>
</div>
```
### Expressões
Você pode incluir expressões Rust arbitrárias dentro do RSX, mas deve escapá-las entre colchetes `[]`:
```rust
{{#include ../../examples/rsx_overview.rs:expression}}
```
```html
<span>DIOXUS</span>
```

View file

@ -0,0 +1,62 @@
# Atributos Especiais
Enquanto a maioria dos atributos são simplesmente passados para o HTML, alguns têm comportamentos especiais.
## A Escotilha de Escape do HTML
Se você estiver trabalhando com itens pré-renderizados, modelos ou uma biblioteca JS, convém passar o HTML diretamente em vez de passar pelo Dioxus. Nesses casos, use `dangerous_inner_html`.
Por exemplo, enviar um conversor de markdown para Dioxus pode aumentar significativamente o tamanho final do aplicativo. Em vez disso, você desejará pré-renderizar sua remarcação para HTML e, em seguida, incluir o HTML diretamente em sua saída. Usamos essa abordagem para a [página inicial do Dioxus](https://dioxuslabs.com):
```rust
{{#include ../../examples/dangerous_inner_html.rs:dangerous_inner_html}}
```
> Nota! Esse atributo é chamado de "dangerous_inner_html" porque é **perigoso** passar dados que você não confia. Se você não for cuidadoso, poderá facilmente expor ataques de [cross-site scripting (XSS)](https://en.wikipedia.org/wiki/Cross-site_scripting) aos seus usuários.
>
> Se você estiver lidando com entradas não confiáveis, certifique-se de higienizar seu HTML antes de passá-lo para `dangerous_inner_html` ou apenas passe-o para um elemento de texto para escapar de qualquer tag HTML.
## Atributos Booleanos
A maioria dos atributos, quando renderizados, serão renderizados exatamente como a entrada que você forneceu. No entanto, alguns atributos são considerados atributos "booleanos" e apenas sua presença determina se eles afetam a saída. Para esses atributos, um valor fornecido de `"false"` fará com que eles sejam removidos do elemento de destino.
Portanto, este RSX não renderizaria o atributo `hidden`:
```rust
{{#include ../../examples/boolean_attribute.rs:boolean_attribute}}
```
```html
<div>hello</div>
```
No entanto, nem todos os atributos funcionam assim. _Apenas os seguintes atributos_ têm este comportamento:
- `allowfullscreen`
- `allowpaymentrequest`
- `async`
- `autofocus`
- `autoplay`
- `checked`
- `controls`
- `default`
- `defer`
- `disabled`
- `formnovalidate`
- `hidden`
- `ismap`
- `itemscope`
- `loop`
- `multiple`
- `muted`
- `nomodule`
- `novalidate`
- `open`
- `playsinline`
- `readonly`
- `required`
- `reversed`
- `selected`
- `truespeed`
Para quaisquer outros atributos, um valor de `"false"` será enviado diretamente para o DOM.

View file

@ -0,0 +1,46 @@
# Aplicativo de área de trabalho
Crie um aplicativo de desktop nativo autônomo que tenha a mesma aparência em todos os sistemas operacionais.
Os aplicativos criados com o Dioxus geralmente têm menos de 5 MB de tamanho e usam os recursos existentes do sistema, para que não consumam quantidades extremas de RAM ou memória.
Exemplos:
- [Explorador de arquivos](https://github.com/DioxusLabs/example-projects/blob/master/file-explorer)
- [Scanner WiFi](https://github.com/DioxusLabs/example-projects/blob/master/wifi-scanner)
[![Exemplo do Explorador de Arquivos](https://raw.githubusercontent.com/DioxusLabs/example-projects/master/file-explorer/image.png)](https://github.com/DioxusLabs/example-projects/tree /master/file-explorer)
## Suporte
O desktop é uma plataforma poderosa para o Dioxus, mas atualmente é limitado em capacidade quando comparado à plataforma Web. Atualmente, os aplicativos de desktop são renderizados com a biblioteca WebView da própria plataforma, mas seu código Rust está sendo executado nativamente em um _thread_ nativo. Isso significa que as APIs do navegador _não_ estão disponíveis, portanto, renderizar WebGL, Canvas, etc. não é tão fácil quanto a Web. No entanto, as APIs do sistema nativo _são_ acessíveis, portanto, streaming, WebSockets, sistema de arquivos, etc., são todas APIs viáveis. No futuro, planejamos migrar para um renderizador DOM personalizado baseado em webrenderer com integrações WGPU.
O Dioxus Desktop é construído a partir do [Tauri](https://tauri.app/). No momento, não há abstrações do Dioxus sobre atalhos de teclado, barra de menus, manuseio, etc., então você deve aproveitar o Tauri - principalmente [Wry](http://github.com/tauri-apps/wry/) e [ Tao](http://github.com/tauri-apps/tao)) diretamente.
## Criando um projeto
Crie uma nova caixa:
```shell
cargo new --bin demo
cd demo
```
Adicione o Dioxus com o recurso `desktop` (isso irá editar o `Cargo.toml`):
```shell
cargo add dioxus --features desktop
```
> Se seu sistema não fornece a biblioteca `libappindicator3`, como Debian/bullseye, você pode habilitar a substituta `ayatana` com um _flag_ adicional:
>
> ```shell
> # On Debian/bullseye use:
> cargo add dioxus --features desktop --features ayatana
> ```
Edite seu `main.rs`:
```rust
{{#include ../../examples/hello_world_desktop.rs:all}}
```

View file

@ -0,0 +1,31 @@
# Configurando o Hot Reload
1. O recarregamento em tempo-real (_hot reload_) permite tempos de iteração muito mais rápidos dentro de chamadas 'rsx', interpretando-as e transmitindo as edições.
2. É útil para alterar o estilo/layout de um programa, mas não ajudará na alteração da lógica de um programa.
3. Atualmente, o cli implementa apenas o _hot-reload_ para o renderizador da web.
# Configurar
Instale o [dioxus-cli](https://github.com/DioxusLabs/cli).
Habilite o recurso de _hot-reload_ no dioxus:
```toml
dioxus = { version = "*", features = ["web", "hot-reload"] }
```
# Usage
1. Execute:
```
dioxus serve --hot-reload
```
2. alterar algum código dentro de uma macro `rsx`
3. abra seu `localhost` em um navegador
4. salve e observe a mudança de estilo sem recompilar
# Limitações
1. O interpretador só pode usar expressões que existiam na última recompilação completa. Se você introduzir uma nova variável ou expressão na chamada `rsx`, ela acionará uma recompilação completa para capturar a expressão.
2. Componentes e Iteradores podem conter código de Rust arbitrário e acionarão uma recompilação completa quando alterados.

View file

@ -0,0 +1,72 @@
# Introdução
Esta seção irá ajudá-lo a configurar seu projeto Dioxus!
## Pré-requisitos
### Editor
O Dioxus se integra muito bem com o [plugin Rust-Analyzer LSP](https://rust-analyzer.github.io) que fornecerá realce de sintaxe apropriado, navegação de código, _folding_ e muito mais.
### Rust
Vá para [https://rust-lang.org](http://rust-lang.org) e instale o compilador Rust.
É altamente recomendável ler o [livro oficial do Rust](https://doc.rust-lang.org/book/ch01-00-getting-started.html) _completamente_. No entanto, nossa esperança é que um aplicativo Dioxus possa servir como um ótimo primeiro projeto Rust. Com Dioxus, você aprenderá sobre:
- Manipulação de erros
- Estruturas, Funções, Enums
- Closures
- Macros
Nós empenhamos muito cuidado para tornar a sintaxe do Dioxus familiar e fácil de entender, para que você não precise de conhecimento profundo sobre _async_, _lifetime_ ou _smart pointers_ até que você realmente comece a criar aplicativos Dioxus complexos.
### Dependências Específicas da Plataforma
#### Windows
Os aplicativos da área de trabalho do Windows dependem do WebView2 uma biblioteca que deve ser instalada em todas as distribuições modernas do Windows. Se você tiver o Edge instalado, o Dioxus funcionará bem. Se você _não_ tiver o Webview2, [você poderá instalá-lo pela Microsoft](https://developer.microsoft.com/en-us/microsoft-edge/webview2/). MS oferece 3 opções:
1. Um pequeno _bootstrapper_ "evergreen" que buscará um instalador do CDN da Microsoft
2. Um pequeno _instalador_ que buscará o Webview2 do CDN da Microsoft
3. Uma versão vinculada estaticamente do Webview2 em seu binário final para usuários offline
Para fins de desenvolvimento, use a Opção 1.
#### Linux
Os aplicativos Webview Linux requerem WebkitGtk. Ao distribuir, isso pode ser parte de sua árvore de dependência em seu `.rpm` ou `.deb`. No entanto, é muito provável que seus usuários já tenham o WebkitGtk.
```bash
sudo apt install libwebkit2gtk-4.0-dev libgtk-3-dev libappindicator3-dev
```
Ao usar o Debian/bullseye, o `libappindicator3-dev` não está mais disponível, pois foi substituído por `libayatana-appindicator3-dev`.
```bash
# on Debian/bullseye use:
sudo apt install libwebkit2gtk-4.0-dev libgtk-3-dev libayatana-appindicator3-dev
```
Se você tiver problemas, certifique-se de ter todo o básico instalado, conforme descrito nos [documentos do Tauri](https://tauri.studio/v1/guides/getting-started/prerequisites#setting-up-linux).
#### Mac OS
Atualmente tudo para macOS está integrado! No entanto, você pode encontrar um problema se estiver usando o Rust **nightly** devido a alguns problemas de permissão em nossa dependência do `Tao` (que foram resolvidos, mas não publicados).
### Extensões do Cargo Sugeridas
Se você quiser manter seu fluxo de trabalho tradicional como `npm install XXX` para adicionar pacotes, você pode querer instalar o `cargo-edit` e algumas outras extensões `cargo` interessantes:
- [cargo-expand](https://github.com/dtolnay/cargo-expand) para expandir chamadas de macro
- [árvore de carga](https://doc.rust-lang.org/cargo/commands/cargo-tree.html) um comando de carga integrado que permite inspecionar sua árvore de dependência
## Guias de configuração
Dioxus suporta múltiplas plataformas. Dependendo do que você quer, a configuração é um pouco diferente.
- [Web](web.md): rodando no navegador usando WASM
- [Server Side Rendering](ssr.md): renderiza Dioxus HTML como texto
- [Desktop](desktop.md): um aplicativo autônomo usando o webview
- [Celular](mobile.md)
- [Terminal UI](tui.md): interface gráfica baseada em texto do terminal

View file

@ -0,0 +1,75 @@
# Aplicativo móvel
Crie um aplicativo móvel com Dioxus!
Exemplo: [Aplicativo Todo](https://github.com/DioxusLabs/example-projects/blob/master/ios_demo)
## Suporte
Atualmente, a plataforma móvel é o destino de renderizador menos suportado para o Dioxus. Os aplicativos móveis são renderizados com o WebView da plataforma, o que significa que animações, transparência e widgets nativos não estão disponíveis no momento.
Além disso, o iOS é a única plataforma móvel compatível. É possível obter o Dioxus rodando no Android e renderizado com WebView, mas a biblioteca de janelas do Rust que o Dioxus usa tao atualmente não suporta Android.
Atualmente, o suporte móvel é mais adequado para aplicativos no estilo CRUD, idealmente para equipes internas que precisam desenvolver rapidamente, mas não se importam muito com animações ou widgets nativos.
## Configurando
A configuração com dispositivos móveis pode ser bastante desafiadora. As ferramentas aqui não são ótimas (ainda) e podem precisar de alguns _hacks_ para fazer as coisas funcionarem. O macOS M1 é amplamente inexplorado e pode não funcionar para você.
Vamos usar `cargo-mobile` para construir para dispositivos móveis. Primeiro, instale-o:
```shell
cargo install --git https://github.com/BrainiumLLC/cargo-mobile
```
E, em seguida, inicialize seu aplicativo para a plataforma certa. Use o modelo `winit` por enquanto. No momento, não há modelo "Dioxus" no cargo-mobile.
```shell
cargo mobile init
```
Nós vamos limpar completamente as `dependências` que ele gera para nós, trocando `winit` por `dioxus-mobile`.
```toml
[package]
name = "dioxus-ios-demo"
version = "0.1.0"
authors = ["Jonathan Kelley <jkelleyrtp@gmail.com>"]
edition = "2018"
# leave the `lib` declaration
[lib]
crate-type = ["staticlib", "cdylib", "rlib"]
# leave the binary it generates for us
[[bin]]
name = "dioxus-ios-demo-desktop"
path = "gen/bin/desktop.rs"
# clear all the dependencies
[dependencies]
mobile-entry-point = "0.1.0"
dioxus = { version = "*", features = ["mobile"] }
simple_logger = "*"
```
Edite seu `lib.rs`:
```rust
use dioxus::prelude::*;
fn main() {
dioxus::mobile::launch(app);
}
fn app(cx: Scope) -> Element {
cx.render(rsx!{
div {
"hello world!"
}
})
}
```

View file

@ -0,0 +1,108 @@
# Renderização por Servidor
O Dioxus 'VirtualDom' pode ser renderizado por servidor.
[Exemplo: Dioxus DocSite](https://github.com/dioxusLabs/docsite)
## Suporte a Multitarefas
O Dioxus `VirtualDom`, infelizmente, atualmente não é `Send`. Internamente, usamos um pouco de mutabilidade interior que não é _thread-safe_. Isso significa que você não pode usar Dioxus facilmente com a maioria dos frameworks da web como Tide, Rocket, Axum, etc.
Para resolver isso, você deve gerar um `VirtualDom` em seu próprio thread e se comunicar com ele por meio de canais.
Ao trabalhar com frameworks web que requerem `Send`, é possível renderizar um `VirtualDom` imediatamente para uma `String` mas você não pode manter o `VirtualDom` em um ponto de espera. Para SSR de estado retido (essencialmente LiveView), você precisará criar um _pool_ de `VirtualDoms`.
## Configurar
Se você quer apenas renderizar `rsx!` ou um VirtualDom para HTML, confira os documentos da API. É bem simples:
```rust
// We can render VirtualDoms
let mut vdom = VirtualDom::new(app);
let _ = vdom.rebuild();
println!("{}", dioxus::ssr::render_vdom(&vdom));
// Or we can render rsx! calls directly
println!( "{}", dioxus::ssr::render_lazy(rsx! { h1 { "Hello, world!" } } );
```
No entanto, para este guia, vamos mostrar como usar Dioxus SSR com `Axum`.
Certifique-se de ter o Rust and Cargo instalado e, em seguida, crie um novo projeto:
```shell
cargo new --bin demo
cd app
```
Adicione o Dioxus com o recurso `ssr`:
```shell
cargo add dioxus --features ssr
```
Em seguida, adicione todas as dependências do Axum. Isso será diferente se você estiver usando um Web Framework diferente
```
cargo add tokio --features full
cargo add axum
```
Suas dependências devem ficar mais ou menos assim:
```toml
[dependencies]
axum = "0.4.5"
dioxus = { version = "*", features = ["ssr"] }
tokio = { version = "1.15.0", features = ["full"] }
```
Agora, configure seu aplicativo Axum para responder em um _endpoint_.
```rust
use axum::{response::Html, routing::get, Router};
use dioxus::prelude::*;
#[tokio::main]
async fn main() {
let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 3000));
println!("listening on http://{}", addr);
axum::Server::bind(&addr)
.serve(
Router::new()
.route("/", get(app_endpoint))
.into_make_service(),
)
.await
.unwrap();
}
```
E, em seguida, adicione nosso _endpoint_. Podemos renderizar `rsx!` diretamente:
```rust
async fn app_endpoint() -> Html<String> {
Html(dioxus::ssr::render_lazy(rsx! {
h1 { "hello world!" }
}))
}
```
Ou podemos renderizar `VirtualDoms`.
```rust
async fn app_endpoint() -> Html<String> {
fn app(cx: Scope) -> Element {
cx.render(rsx!(h1 { "hello world" }))
}
let mut app = VirtualDom::new(app);
let _ = app.rebuild();
Html(dioxus::ssr::render_vdom(&app))
}
```
E é isso!
> Você pode notar que não pode manter o VirtualDom em um ponto de espera. Dioxus atualmente não é ThreadSafe, então _deve_ permanecer no _thread_ que iniciou. Estamos trabalhando para flexibilizar essa exigência.

View file

@ -0,0 +1,45 @@
# IU do terminal
Você pode construir uma interface baseada em texto que será executada no terminal usando o Dioxus.
![Hello World screenshot](https://github.com/DioxusLabs/rink/raw/master/examples/example.png)
> Nota: este livro foi escrito tendo em mente plataformas baseadas em HTML. Você pode acompanhar a TUI, mas terá que se adaptar um pouco.
## Suporte
O suporte à TUI é atualmente bastante experimental. Até o nome do projeto mudará. Mas, se você estiver disposto a se aventurar no reino do desconhecido, este guia o ajudará a começar.
## Configurando
Comece criando um novo pacote e adicionando nosso recurso TUI.
```shell
cargo new --bin demo
cd demo
cargo add dioxus --features tui
```
Em seguida, edite seu `main.rs` com o modelo básico.
```rust
{{#include ../../examples/hello_world_tui.rs}}
```
Para executar nosso aplicativo:
```shell
cargo run
```
Pressione "ctrl-c" para fechar o aplicativo. Para mudar de "ctrl-c" para apenas "q" para sair, você pode iniciar o aplicativo com uma configuração para desativar o sair padrão e usar a raiz TuiContext para sair por conta própria.
```rust
{{#include ../../examples/hello_world_tui_no_ctrl_c.rs}}
```
## Notas
- Nosso pacote TUI usa flexbox para layout
- 1px é a altura da linha de um caractere. Seu px CSS regular não traduz.
- Se seu aplicativo entrar em pânico, seu terminal será destruído. Isso será corrigido eventualmente.

View file

@ -0,0 +1,74 @@
# Web
Crie aplicativos de página única (SPA) que são executados no navegador com o Dioxus. Para rodar na Web, seu aplicativo deve ser compilado para WebAssembly e utilizar a `crate` `dioxus` com o recurso `web` ativado.
Uma compilação do Dioxus para a web será aproximadamente equivalente ao tamanho de uma compilação do React (70kb vs 65kb), mas carregará significativamente mais rápido devido ao [StreamingCompile do WebAssembly](https://hacks.mozilla.org/2018/01/making-webassembly-even-faster-firefoxs-new-streaming-and-tiering-compiler/).
Exemplos:
- [TodoMVC](https://github.com/DioxusLabs/example-projects/tree/master/todomvc)
- [ECommerce](https://github.com/DioxusLabs/example-projects/tree/master/ecommerce-site)
[![Exemplo de TodoMVC](https://github.com/DioxusLabs/example-projects/raw/master/todomvc/example.png)](https://github.com/DioxusLabs/example-projects/blob/master /todomvc)
> Nota: Devido às limitações do Wasm, nem todos as `crates` funcionarão com seus aplicativos da web, portanto, você precisará certificar-se de que suas `crates` funcionem sem chamadas de sistema nativas (temporizadores, IO, etc.).
## Suporte
A Web é a plataforma de destino com melhor suporte para Dioxus.
## Ferramentas
Para desenvolver seu aplicativo Dioxus para a web, você precisará de uma ferramenta para construir e servir seus itens. Recomendamos usar [trunk](https://trunkrs.dev) que inclui um sistema de compilação, otimização Wasm, um servidor dev e suporte para SASS/CSS:
```shell
cargo install trunk
```
Certifique-se de que o destino `wasm32-unknown-unknown` esteja instalado:
```shell
rustup target add wasm32-unknown-unknown
```
## Criando um Projeto
Crie uma nova caixa:
```shell
cargo new --bin demo
cd demo
```
Adicione o Dioxus como uma dependência com o recurso `web`:
```bash
cargo add dioxus --features web
```
Adicione um `index.html` para o `Trunk` usar. Certifique-se de que seu elemento "mount point" tenha um ID de "main":
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body>
<div id="main"></div>
</body>
</html>
```
Edite seu `main.rs`:
```rust
{{#include ../../examples/hello_world_web.rs}}
```
E para servir nosso aplicativo:
```bash
trunk serve
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 KiB

View file

@ -0,0 +1,52 @@
# Introdução
![dioxuslogo](./images/dioxuslogo_full.png)
Dioxus é uma estrutura portátil, de alto desempenho e ergonômica para a construção de interfaces de usuário multiplataforma no Rust. Este guia irá ajudá-lo a começar a escrever aplicativos Dioxus para a Web, Desktop, Mobile e muito mais.
```rust
fn app(cx: Scope) -> Element {
let mut count = use_state(&cx, || 0);
cx.render(rsx!(
h1 { "High-Five counter: {count}" }
button { onclick: move |_| count += 1, "Up high!" }
button { onclick: move |_| count -= 1, "Down low!" }
))
}
```
Dioxus é fortemente inspirado pelo React. Se você conhece o React, começar com o Dioxus será muito fácil.
> Este guia pressupõe que você já conhece um pouco de [Rust](https://www.rust-lang.org/)! Caso contrário, recomendamos ler [_the book_](https://doc.rust-lang.org/book/ch01-00-getting-started.html) para aprender Rust primeiro.
## Recursos
- Aplicativos de desktop rodando nativamente (sem Electron!) em menos de 10 linhas de código.
- Gerenciamento de estado incrivelmente ergonômico e poderoso.
- Documentação em linha abrangente - documentação sob o mouse e guias para todos os elementos HTML e manipuladores de eventos.
- Extremamente eficiente de memória 0 alocações globais para componentes de estado estacionário.
- Agendador assíncrono multicanal para suporte assíncrono de primeira classe.
- E mais! Leia as [notas de lançamento completa](https://dioxuslabs.com/blog/introducing-dioxus/.
### Multi Plataforma
Dioxus é um kit de ferramentas _portátil_, o que significa que a implementação do núcleo pode ser executada em qualquer lugar sem independente da plataforma. Ao contrário de muitos outros kits de ferramentas de frontend Rust, o Dioxus não está intrinsecamente vinculado ao WebSys. Na verdade, todos os elementos e ouvintes de eventos podem ser trocados em tempo de compilação. Por padrão, o Dioxus vem com o recurso `html` habilitado, mas isso pode ser desabilitado dependendo do seu renderizador de destino.
No momento, temos vários renderizadores de terceiros:
- WebSys (para WASM): Ótimo suporte
- Tao/Tokio (para aplicativos de desktop): Bom suporte
- Tao/Tokio (para aplicativos móveis): Suporte ruim
- SSR (para gerar marcação estática)
- TUI/Rink (para aplicativos baseados em terminal): Experimental
## Estabilidade
Dioxus ainda não chegou a uma versão estável.
Web: como a Web é uma plataforma bastante madura, esperamos que haja muito pouca rotatividade de API para recursos baseados na Web.
Desktop: APIs provavelmente estarão em fluxo à medida que descobrimos padrões melhores do que nossa contraparte, ElectronJS.
SSR: Não esperamos que a API SSR mude drasticamente no futuro.

View file

@ -0,0 +1,26 @@
# Hooks Personalizados
_Hooks_ são uma ótima maneira de encapsular a lógica de negócio. Se nenhum dos _hooks_ existentes funcionar para o seu problema, você pode escrever o seu próprio.
## Hooks de Composição
Para evitar a repetição, você pode encapsular a lógica de negócios com base em _hooks_ existentes para criar um novo gancho.
Por exemplo, se muitos componentes precisam acessar uma _struct_ `AppSettings`, você pode criar um gancho de "atalho":
```rust
{{#include ../../examples/hooks_composed.rs:wrap_context}}
```
## Lógica de Hook Personalizada
Você pode usar [`cx.use_hook`](https://docs.rs/dioxus/latest/dioxus/prelude/struct.Scope.html#method.use_hook) para construir seus próprios _hooks_. Na verdade, é nisso que todos os _hooks_ padrão são construídos!
`use_hook` aceita um único encerramento para inicializar o _hook_. Ele será executado apenas na primeira vez que o componente for renderizado. O valor de retorno desse encerramento será usado como o valor do _hook_ o Dioxus o pegará e o armazenará enquanto o componente estiver vivo. Em cada renderização (não apenas na primeira!), você receberá uma referência a esse valor.
> Nota: Você pode implementar [`Drop`](https://doc.rust-lang.org/std/ops/trait.Drop.html) para o valor do seu _hook_ ele será descartado e o componente será desmontado (não mais na interface do usuário)
Dentro do encerramento de inicialização, você normalmente fará chamadas para outros métodos `cx`. Por exemplo:
- O _hook_ `use_state` rastreia o estado no valor do _hook_ e usa [`cx.schedule_update`](https://docs.rs/dioxus/latest/dioxus/prelude/struct.Scope.html#method.schedule_update) para o Dioxus renderizar novamente o componente sempre que ele for alterado.
- O _hook_ `use_context` chama [`cx.consume_context`](https://docs.rs/dioxus/latest/dioxus/prelude/struct.Scope.html#method.consume_context) (que seria custoso chamar em cada render) para obter algum contexto do escopo

View file

@ -0,0 +1,71 @@
# Renderização Dinâmica
Às vezes você quer renderizar coisas diferentes dependendo do estado/_props_. Com o Dioxus, apenas descreva o que você quer ver usando o fluxo de controle do Rust o _framework_ se encarregará de fazer as mudanças necessárias em tempo real se o estado ou _props_ mudarem!
## Renderização Condicional
Para renderizar diferentes elementos com base em uma condição, você pode usar uma instrução `if-else`:
```rust
{{#include ../../examples/conditional_rendering.rs:if_else}}
```
> Você também pode usar instruções `match`, ou qualquer função Rust para renderizar condicionalmente coisas diferentes.
### Inspecionando _props_ `Element`
Como `Element` é uma `Option<VNode>`, os componentes que aceitam `Element` como _prop_ podem realmente inspecionar seu conteúdo e renderizar coisas diferentes com base nisso. Exemplo:
```rust
{{#include ../../examples/component_children_inspect.rs:Clickable}}
```
Você não pode modificar o `Element`, mas se precisar de uma versão modificada dele, você pode construir um novo baseado em seus atributos/filhos/etc.
## Renderizando Nada
Para renderizar nada, você pode retornar `None` de um componente. Isso é útil se você deseja ocultar algo condicionalmente:
```rust
{{#include ../../examples/conditional_rendering.rs:conditional_none}}
```
Isso funciona porque o tipo `Element` é apenas um alias para `Option<VNode>`
> Novamente, você pode usar um método diferente para retornar condicionalmente `None`. Por exemplo, a função _booleana_ [`then()`](https://doc.rust-lang.org/std/primitive.bool.html#method.then) pode ser usada.
## Listas de renderização
Frequentemente, você desejará renderizar uma coleção de componentes. Por exemplo, você pode querer renderizar uma lista de todos os comentários em uma postagem.
Para isso, o Dioxus aceita iteradores que produzem `Element`s. Então precisamos:
- Obter um iterador sobre todos os nossos itens (por exemplo, se você tiver um `Vec` de comentários, itere sobre ele com `iter()`)
- `.map` o iterador para converter cada item em um `Element` renderizado usando `cx.render(rsx!(...))`
- Adicione um atributo `key` exclusivo a cada item do iterador
- Incluir este iterador no RSX final
Exemplo: suponha que você tenha uma lista de comentários que deseja renderizar. Então, você pode renderizá-los assim:
```rust
{{#include ../../examples/rendering_lists.rs:render_list}}
```
### O Atributo `key`
Toda vez que você renderiza novamente sua lista, o Dioxus precisa acompanhar qual item foi para onde, porque a ordem dos itens em uma lista pode mudar itens podem ser adicionados, removidos ou trocados. Apesar disso, Dioxus precisa:
- Acompanhar o estado do componente
- Descobrir com eficiência quais atualizações precisam ser feitas na interface do usuário
Por exemplo, suponha que o `CommentComponent` tenha algum estado ex. um campo onde o usuário digitou uma resposta. Se a ordem dos comentários mudar repentinamente, o Dioxus precisa associar corretamente esse estado ao mesmo comentário caso contrário, o usuário acabará respondendo a um comentário diferente!
Para ajudar o Dioxus a acompanhar os itens da lista, precisamos associar cada item a uma chave exclusiva. No exemplo acima, geramos dinamicamente a chave exclusiva. Em aplicações reais, é mais provável que a chave venha de, por exemplo, um ID de banco de dados. Realmente não importa de onde você obtém a chave, desde que atenda aos requisitos
- As chaves devem ser únicas em uma lista
- O mesmo item deve sempre ser associado à mesma chave
- As chaves devem ser relativamente pequenas (ou seja, converter toda a estrutura Comment em uma String seria uma chave muito ruim) para que possam ser comparadas com eficiência
Você pode ficar tentado a usar o índice de um item na lista como sua chave. Na verdade, é isso que o Dioxus usará se você não especificar uma chave. Isso só é aceitável se você puder garantir que a lista seja constante ou seja, sem reordenação, adições ou exclusões.
> Observe que se você passar a chave para um componente que você criou, ele não receberá a chave como _prop_. É usado apenas como uma dica pelo próprio Dioxus. Se o seu componente precisar de um ID, você deve passá-lo como um _prop_ separado.

View file

@ -0,0 +1,66 @@
# Manipuladores de eventos
Eventos são ações interessantes que acontecem, geralmente relacionadas a algo que o usuário fez. Por exemplo, alguns eventos acontecem quando o usuário clica, rola, move o mouse ou digita um caractere. Para responder a essas ações e tornar a interface do usuário interativa, precisamos lidar com esses eventos.
Os eventos estão associados a elementos. Por exemplo, geralmente não nos importamos com todos os cliques que acontecem em um aplicativo, apenas aqueles em um botão específico. Para lidar com eventos que acontecem em um elemento, devemos anexar o manipulador de eventos desejado a ele.
Os manipuladores de eventos são semelhantes aos atributos regulares, mas seus nomes geralmente começam com `on` - e aceitam encerramentos como valores. O encerramento será chamado sempre que o evento for acionado e será passado esse evento.
Por exemplo, para manipular cliques em um elemento, podemos especificar um manipulador `onclick`:
```rust
{{#include ../../examples/event_click.rs:rsx}}
```
## O Objeto `Evento`
Os manipuladores de eventos recebem um objeto [`UiEvent`](https://docs.rs/dioxus-core/latest/dioxus_core/struct.UiEvent.html) contendo informações sobre o evento. Diferentes tipos de eventos contêm diferentes tipos de dados. Por exemplo, eventos relacionados ao mouse contêm [`MouseData`](https://docs.rs/dioxus/latest/dioxus/events/struct.MouseData.html), que informa coisas como onde o mouse foi clicado e quais botões do mouse foram usados.
No exemplo acima, esses dados de evento foram registrados no terminal:
```
Clicked! Event: UiEvent { data: MouseData { coordinates: Coordinates { screen: (468.0, 109.0), client: (73.0, 25.0), element: (63.0, 15.0), page: (73.0, 25.0) }, modifiers: (empty), held_buttons: EnumSet(), trigger_button: Some(Primary) } }
Clicked! Event: UiEvent { data: MouseData { coordinates: Coordinates { screen: (468.0, 109.0), client: (73.0, 25.0), element: (63.0, 15.0), page: (73.0, 25.0) }, modifiers: (empty), held_buttons: EnumSet(), trigger_button: Some(Primary) } }
```
Para saber o que os diferentes tipos de eventos fornecem, leia os [documentos do módulo de eventos](https://docs.rs/dioxus/latest/dioxus/events/index.html).
### Parando a propagação
Quando você tem, por exemplo um `button` dentro de um `div`, qualquer clique no `button` também é um clique no `div`. Por esta razão, Dioxus propaga o evento click: primeiro, ele é acionado no elemento alvo, depois nos elementos pai. Se você quiser evitar esse comportamento, você pode chamar `cancel_bubble()` no evento:
```rust
{{#include ../../examples/event_click.rs:rsx}}
```
## Prevenir o Padrão
Alguns eventos têm um comportamento padrão. Para eventos de teclado, isso pode ser inserir o caractere digitado. Para eventos de mouse, isso pode estar selecionando algum texto.
Em alguns casos, você pode querer evitar esse comportamento padrão. Para isso, você pode adicionar o atributo `prevent_default` com o nome do manipulador cujo comportamento padrão você deseja interromper. Este atributo é especial: você pode anexá-lo várias vezes para vários atributos:
```rust
{{#include ../../examples/event_prevent_default.rs:prevent_default}}
```
Quaisquer manipuladores de eventos ainda serão chamados.
> Normalmente, em React ou JavaScript, você chamaria "preventDefault" no evento no retorno de chamada. Dioxus atualmente _não_ suporta este comportamento. Observação: isso significa que você não pode impedir condicionalmente o comportamento padrão.
## Manipulador de Props
Às vezes, você pode querer criar um componente que aceite um manipulador de eventos. Um exemplo simples seria um componente `FancyButton`, que aceita um manipulador `on_click`:
```rust
{{#include ../../examples/event_handler_prop.rs:component_with_handler}}
```
Então, você pode usá-lo como qualquer outro manipulador:
```rust
{{#include ../../examples/event_handler_prop.rs:usage}}
```
> Nota: assim como qualquer outro atributo, você pode nomear os manipuladores como quiser! Embora eles devam começar com `on`, para que o prop seja automaticamente transformado em um `EventHandler` no local da chamada.
>
> Você também pode colocar dados personalizados no evento, em vez de, por exemplo, `MouseData`

View file

@ -0,0 +1,87 @@
# Hooks e Estado do Componente
Até agora nossos componentes, sendo funções Rust, não tinham estado eles estavam sempre renderizando a mesma coisa. No entanto, em um componente de interface do usuário, geralmente é útil ter uma funcionalidade com estado para criar interações do usuário. Por exemplo, você pode querer rastrear se o usuário abriu uma lista suspensa e renderizar coisas diferentes de acordo.
Para lógica com estado, você pode usar _hooks_. _Hooks_ são funções Rust que fazem referência a `ScopeState` (em um componente, você pode passar `&cx`), e fornecem funcionalidade e estado.
## Hook `use_state`
[`use_state`](https://docs.rs/dioxus/latest/dioxus/hooks/fn.use_state.html) é um dos _hooks_ mais simples.
- Você fornece um fechamento que determina o valor inicial
- `use_state` fornece o valor atual e uma maneira de atualizá-lo, definindo-o para outra coisa
- Quando o valor é atualizado, `use_state` faz o componente renderizar novamente e fornece o novo valor
Por exemplo, você pode ter visto o exemplo do contador, no qual o estado (um número) é rastreado usando o _hook_ `use_state`:
```rust
{{#include ../../examples/hooks_counter.rs:component}}
```
![Screenshot: counter app](./images/counter.png)
Toda vez que o estado do componente muda, ele é renderizado novamente e a função do componente é chamada, para que você possa descrever como deseja que a nova interface do usuário se pareça. Você não precisa se preocupar em "mudar" nada - apenas descreva o que você quer em termos de estado, e Dioxus cuidará do resto!
> `use_state` retorna seu valor envolto em uma _smart pointer_ do tipo [`UseState`](https://docs.rs/dioxus/latest/dioxus/hooks/struct.UseState.html). É por isso que você pode ler o valor e atualizá-lo, mesmo dentro de um manipulador.
Você pode usar vários _hooks_ no mesmo componente se quiser:
```rust
{{#include ../../examples/hooks_counter_two_state.rs:component}}
```
![Screenshot: app with two counters](./images/counter_two_state.png)
## Regras dos Hooks
O exemplo acima pode parecer um pouco mágico, já que as funções Rust normalmente não estão associadas ao estado. O Dioxus permite que os _hooks_ mantenham o estado entre as renderizações através de uma referência a `ScopeState`, e é por isso que você deve passar `&cx` para eles.
Mas como Dioxus pode diferenciar entre vários _hooks_ no mesmo componente? Como você viu no segundo exemplo, ambas as funções `use_state` foram chamadas com os mesmos parâmetros, então como elas podem retornar coisas diferentes quando os contadores são diferentes?
```rust
{{#include ../../examples/hooks_counter_two_state.rs:use_state_calls}}
```
Isso só é possível porque os dois _hooks_ são sempre chamados na mesma ordem, então Dioxus sabe qual é qual. Portanto, a ordem em que você chama os _hooks_ é importante, e é por isso que você deve seguir certas regras ao usar os _hooks_:
1. _Hooks_ só podem ser usados em componentes ou outros _hooks_ (falaremos disso mais tarde)
2. Em cada chamada para a função componente
1. Os mesmos _hooks_ devem ser chamados
2. Na mesma ordem
3. Os nomes dos _hooks_ devem começar com `use_` para que você não os confunda acidentalmente com funções normais
Essas regras significam que há certas coisas que você não pode fazer com _hooks_:
### Sem Hooks em Condicionais
```rust
{{#include ../../examples/hooks_bad.rs:conditional}}
```
### Sem Hooks em Closures
```rust
{{#include ../../examples/hooks_bad.rs:closure}}
```
### Sem Hooks em Loops
```rust
{{#include ../../examples/hooks_bad.rs:loop}}
```
## Gancho `use_ref`
`use_state` é ótimo para rastrear valores simples. No entanto, você pode notar na [`UseState` API](https://docs.rs/dioxus/latest/dioxus/hooks/struct.UseState.html) que a única maneira de modificar seu valor é substituí-lo por algo else (por exemplo, chamando `set`, ou através de um dos operadores `+=`, `-=`). Isso funciona bem quando é barato construir um valor (como qualquer primitivo). Mas e se você quiser manter dados mais complexos no estado dos componentes?
Por exemplo, suponha que queremos manter um `Vec` de valores. Se o armazenamos com `use_state`, a única maneira de adicionar um novo valor à lista seria criar um novo `Vec` com o valor adicional e colocá-lo no estado. Isto é custoso! Queremos modificar o `Vec` existente.
Felizmente, existe outro _hook_ para isso, `use_ref`! É semelhante ao `use_state`, mas permite obter uma referência mutável aos dados contidos.
Aqui está um exemplo simples que mantém uma lista de eventos em um `use_ref`. Podemos adquirir acesso de escrita ao estado com `.write()`, e então apenas `.push` um novo valor para o estado:
```rust
{{#include ../../examples/hooks_use_ref.rs:component}}
```
> Os valores de retorno de `use_state` e `use_ref`, (`UseState` e `UseRef`, respectivamente) são de alguma forma semelhantes a [`Cell`](https://doc.rust-lang.org/std/ cell/) e [`RefCell`](https://doc.rust-lang.org/std/cell/struct.RefCell.html) eles fornecem mutabilidade interior. No entanto, esses _wrappers_ do Dioxus também garantem que o componente seja renderizado novamente sempre que você alterar o estado.

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 362 KiB

View file

@ -0,0 +1,3 @@
# Interatividade
Até agora, aprendemos como descrever a estrutura e as propriedades de nossas interfaces de usuário. No entanto, a maioria das interfaces precisa ser interativa para ser útil. Neste capítulo, descrevemos como fazer um aplicativo Dioxus que responde ao usuário.

View file

@ -0,0 +1,82 @@
# Roteador
Em muitos de seus aplicativos, você desejará ter diferentes "cenas". Para uma página da Web, essas cenas podem ser as diferentes páginas da Web com seu próprio conteúdo.
Você pode escrever sua própria solução de gerenciamento de cena também. No entanto, para poupar o seu esforço, o Dioxus oferece suporte a uma solução de primeira linha para gerenciamento de cena chamada Dioxus Router.
## O que é isso?
Para um aplicativo como a página de destino do Dioxus (https://dioxuslabs.com), queremos ter páginas diferentes. Um esboço rápido de um aplicativo seria algo como:
- Pagina inicial
- Blogue
- Exemplo de vitrine
Cada uma dessas cenas é independente não queremos renderizar a página inicial e o blog ao mesmo tempo.
É aqui que a `crates` do roteador são úteis. Para ter certeza de que estamos usando o roteador, basta adicionar o recurso `router` à sua dependência do Dioxus.
```toml
[dependencies]
dioxus = { version = "0.2", features = ["desktop", "router"] }
```
## Usando o Roteador
Ao contrário de outros roteadores no ecossistema Rust, nosso roteador é construído de forma declarativa. Isso torna possível compor o layout do nosso aplicativo simplesmente organizando os componentes.
```rust
rsx!{
Router {
Route { to: "/home", Home {} }
Route { to: "/blog", Blog {} }
}
}
```
Sempre que visitamos este aplicativo, obteremos o componente Home ou o componente Blog renderizado, dependendo de qual rota entrarmos. Se nenhuma dessas rotas corresponder à localização atual, nada será renderizado.
Podemos corrigir isso de duas maneiras:
- Uma página 404 de _fallback_
```rust
rsx!{
Router {
Route { to: "/home", Home {} }
Route { to: "/blog", Blog {} }
Route { to: "", NotFound {} }
}
}
```
- Redirecionar 404 para _Home_
```rust
rsx!{
Router {
Route { to: "/home", Home {} }
Route { to: "/blog", Blog {} }
Redirect { from: "", to: "/home" }
}
}
```
## Links
Para que nosso aplicativo navegue nessas rotas, podemos fornecer elementos clicáveis chamados Links. Eles simplesmente envolvem elementos `<a>` que, quando clicados, navegam no aplicativo para o local determinado.
```rust
rsx!{
Link {
to: "/home",
"Go home!"
}
}
```
## Mais leitura
Esta página é apenas uma breve visão geral do roteador para mostrar que existe uma solução poderosa já construída para um problema muito comum. Para obter mais informações sobre o roteador, confira seu livro ou confira alguns dos exemplos.
O roteador tem sua própria documentação! [Disponível aqui](https://dioxuslabs.com/router/guide/).

View file

@ -0,0 +1,71 @@
# Estado de compartilhamento
Muitas vezes, vários componentes precisam acessar o mesmo estado. Dependendo de suas necessidades, existem várias maneiras de implementar isso.
## Elevenado o Estado
Uma abordagem para compartilhar o estado entre os componentes é "elevá-lo" até o ancestral comum mais próximo. Isso significa colocar o _hook_ `use_state` em um componente pai e passar os valores necessários como _props_.
Por exemplo, suponha que queremos construir um editor de memes. Queremos ter uma entrada para editar a legenda do meme, mas também uma visualização do meme com a legenda. Logicamente, o meme e a entrada são 2 componentes separados, mas precisam acessar o mesmo estado (a legenda atual).
> Claro, neste exemplo simples, poderíamos escrever tudo em um componente - mas é melhor dividir tudo em componentes menores para tornar o código mais reutilizável e fácil de manter (isso é ainda mais importante para aplicativos maiores e complexos) .
Começamos com um componente `Meme`, responsável por renderizar um meme com uma determinada legenda:
```rust
{{#include ../../examples/meme_editor.rs:meme_component}}
```
> Observe que o componente `Meme` não sabe de onde vem a legenda ela pode ser armazenada em `use_state`, `use_ref` ou uma constante. Isso garante que seja muito reutilizável - o mesmo componente pode ser usado para uma galeria de memes sem nenhuma alteração!
Também criamos um editor de legendas, totalmente desacoplado do meme. O editor de legendas não deve armazenar a legenda em si caso contrário, como iremos fornecê-la ao componente `Meme`? Em vez disso, ele deve aceitar a legenda atual como um suporte, bem como um manipulador de eventos para delegar eventos de entrada para:
```rust
{{#include ../../examples/meme_editor.rs:caption_editor}}
```
Finalmente, um terceiro componente renderizará os outros dois como filhos. Ele será responsável por manter o estado e passar os _props_ relevantes.
```rust
{{#include ../../examples/meme_editor.rs:meme_editor}}
```
![Captura de tela do editor de memes: Um velho esqueleto de plástico sentado em um banco de parque. Legenda: "eu esperando por um recurso de idioma"](./images/meme_editor_screenshot.png)
## Usando o contexto
Às vezes, algum estado precisa ser compartilhado entre vários componentes na árvore, e passá-lo pelos _props_ é muito inconveniente.
Suponha agora que queremos implementar uma alternância de modo escuro para nosso aplicativo. Para conseguir isso, faremos com que cada componente selecione o estilo dependendo se o modo escuro está ativado ou não.
> Nota: estamos escolhendo esta abordagem como exemplo. Existem maneiras melhores de implementar o modo escuro (por exemplo, usando variáveis CSS). Vamos fingir que as variáveis CSS não existem bem-vindo a 2013!
Agora, poderíamos escrever outro `use_state` no componente superior e passar `is_dark_mode` para cada componente através de _props_. Mas pense no que acontecerá à medida que o aplicativo crescer em complexidade quase todos os componentes que renderizam qualquer CSS precisarão saber se o modo escuro está ativado ou não para que todos precisem do mesmo suporte do modo escuro. E cada componente pai precisará passá-lo para eles. Imagine como isso ficaria confuso e verboso, especialmente se tivéssemos componentes com vários níveis de profundidade!
A Dioxus oferece uma solução melhor do que esta "perfuração com hélice" fornecendo contexto. O _hook_ [`use_context_provider`](https://docs.rs/dioxus-hooks/latest/dioxus_hooks/fn.use_context_provider.html) é semelhante ao `use_ref`, mas o torna disponível através do [`use_context`](https://docs.rs/dioxus-hooks/latest/dioxus_hooks/fn.use_context.html) para todos os componentes filhos.
Primeiro, temos que criar um _struct_ para nossa configuração de modo escuro:
```rust
{{#include ../../examples/meme_editor_dark_mode.rs:DarkMode_struct}}
```
Agora, em um componente de nível superior (como `App`), podemos fornecer o contexto `DarkMode` para todos os componentes filhos:
```rust
{{#include ../../examples/meme_editor_dark_mode.rs:context_provider}}
```
Como resultado, qualquer componente filho de `App` (direto ou não), pode acessar o contexto `DarkMode`.
```rust
{{#include ../../examples/meme_editor_dark_mode.rs:use_context}}
```
> `use_context` retorna `Option<UseSharedState<DarkMode>>` aqui. Se o contexto foi fornecido, o valor é `Some(UseSharedState)`, que você pode chamar `.read` ou `.write`, similarmente a `UseRef`. Caso contrário, o valor é `None`.
Por exemplo, aqui está como implementaríamos a alternância do modo escuro, que lê o contexto (para determinar a cor que deve renderizar) e grava nele (para alternar o modo escuro):
```rust
{{#include ../../examples/meme_editor_dark_mode.rs:toggle}}
```

View file

@ -0,0 +1,33 @@
# Entradas do Usuário
As interfaces geralmente precisam fornecer uma maneira de inserir dados: por exemplo, texto, números, caixas de seleção, etc. No Dioxus, há duas maneiras de trabalhar com a entrada do usuário.
## Entradas Controladas
Com entradas controladas, você fica diretamente responsável pelo estado da entrada. Isso lhe dá muita flexibilidade e facilita manter as coisas em sincronia. Por exemplo, é assim que você criaria uma entrada de texto controlada:
```rust
{{#include ../../examples/input_controlled.rs:component}}
```
Observe a flexibilidade - você pode:
- Exibir o mesmo conteúdo em outro elemento, e eles estarão em sincronia
- Transformar a entrada toda vez que for modificada (por exemplo, para garantir que seja maiúscula)
- Validar a entrada toda vez que ela mudar
- Ter uma lógica personalizada acontecendo quando a entrada for alterada (por exemplo, solicitação de rede para preenchimento automático)
- Alterar programaticamente o valor (por exemplo, um botão "randomize" que preenche a entrada com absurdos)
## Entradas não Controladas
Como alternativa às entradas controladas, você pode simplesmente deixar a plataforma acompanhar os valores de entrada. Se não dissermos a uma entrada HTML qual conteúdo ela deve ter, ela poderá ser editada de qualquer maneira (isso está embutido na visualização da web). Essa abordagem pode ser mais eficiente, mas menos flexível. Por exemplo, é mais difícil manter a entrada sincronizada com outro elemento.
Como você não tem necessariamente o valor atual da entrada não controlada no estado, você pode acessá-lo ouvindo os eventos `oninput` (de maneira semelhante aos componentes controlados) ou, se a entrada for parte de um formulário, você pode acessar os dados do formulário nos eventos do formulário (por exemplo, `oninput` ou `onsubmit`):
```rust
{{#include ../../examples/input_uncontrolled.rs:component}}
```
```
Submitted! UiEvent { data: FormData { value: "", values: {"age": "very old", "date": "1966", "name": "Fred"} } }
```

View file

@ -0,0 +1 @@
// empty (we only need this crate for the examples)

View file

@ -0,0 +1,54 @@
# Publicação
Parabéns! Você fez seu primeiro aplicativo Dioxus que realmente faz coisas muito legais. Este aplicativo usa a biblioteca WebView do seu sistema operacional, portanto, é portátil para ser distribuído para outras plataformas.
Nesta seção, abordaremos como agrupar seu aplicativo para macOS, Windows e Linux.
## Instale o `cargo-bundle`
A primeira coisa que faremos é instalar o [`cargo-bundle`](https://github.com/burtonageo/cargo-bundle). Essa extensão para carga facilitará muito o empacotamento do nosso aplicativo para as várias plataformas.
De acordo com a página do github `cargo-bundle`,
_"cargo-bundle é uma ferramenta usada para gerar instaladores ou pacotes de aplicativos para executáveis GUI criados com o cargo. Ele pode criar pacotes .app para Mac OS X e iOS, pacotes .deb para Linux e instaladores .msi para Windows (observe no entanto que o suporte para iOS e Windows ainda é experimental). O suporte para a criação de pacotes .rpm (para Linux) e pacotes .apk (para Android) ainda está pendente."_
Para instalar, basta executar
`cargo install <name of cargo package>`
## Configurando seu Projeto
Para obter uma configuração de projeto para empacotamento, precisamos adicionar algumas _flags_ ao nosso arquivo `Cargo.toml`.
```toml
[package]
name = "example"
# ...other fields...
[package.metadata.bundle]
name = "DogSearch"
identifier = "com.dogs.dogsearch"
version = "1.0.0"
copyright = "Copyright (c) Jane Doe 2016. All rights reserved."
category = "Developer Tool"
short_description = "Easily search for Dog photos"
long_description = """
This app makes it quick and easy to browse photos of dogs from over 200 bree
"""
```
## Empacotando
Seguindo as instruções do cargo-bundle, simplesmente `cargo-bundle --release` para produzir um aplicativo final com todas as otimizações e recursos integrados.
Depois de executar `cargo-bundle --release`, seu aplicativo deve estar acessível em
`target/release/bundle/<platform>/`.
Por exemplo, um aplicativo macOS ficaria assim:
![Aplicativo publicado](../images/publish.png)
Ótimo! E são apenas 4,8 Mb extremamente enxutos! Como o Dioxus aproveita o WebView nativo da sua plataforma, os aplicativos Dioxus são extremamente eficientes em termos de memória e não desperdiçam sua bateria.
> Nota: nem todo CSS funciona da mesma forma em todas as plataformas. Certifique-se de visualizar o CSS do seu aplicativo em cada plataforma ou navegador da web (Firefox, Chrome, Safari) antes de publicar.

View file

@ -0,0 +1 @@
# Publicando

View file

@ -0,0 +1,9 @@
## Publicando com o Github Pages
Para construir nosso aplicativo e publicá-lo no Github:
- Verifique se o GitHub Pages está configurado para seu repositório
- Crie seu aplicativo com `trunk build --release` (inclua `--public-url <repo-name>` para atualizar os prefixos de ativos se estiver usando um site de projeto)
- Mova seu HTML/CSS/JS/Wasm gerado de `dist` para a pasta configurada para Github Pages
- Adicione e confirme com `git`
- `git push` para o GitHub

View file

@ -0,0 +1,138 @@
# Roteiro e Conjunto de Recursos
Este conjunto de recursos e roteiro podem ajudá-lo a decidir se o que a Dioxus pode fazer hoje funciona para você.
Se um recurso que você precisa não existe ou você deseja contribuir com projetos no roteiro, sinta-se à vontade para se envolver [juntando-se ao discord](https://discord.gg/XgGxMSkvUM).
Em geral, aqui está o status de cada plataforma:
- **Web**: Dioxus é uma ótima opção para aplicativos da web puros - especialmente para aplicativos CRUD/complexos. No entanto, ele não possui o ecossistema do React, então você pode estar perdendo uma biblioteca de componentes ou algum _hook_ útil.
- **SSR**: Dioxus é uma ótima opção para pré-renderização, hidratação e renderização de HTML em um endpoint da web. Esteja ciente: o `VirtualDom` não é (atualmente) `Send + Sync`.
- **Desktop**: você pode criar aplicativos de desktop de janela única muito competentes agora mesmo. No entanto, os aplicativos de várias janelas exigem suporte do núcleo do Dioxus que não estão prontos.
- **Celular**: o suporte móvel é muito recente. Você descobrirá as coisas à medida que avança e não há muitas `crates` de suporte para periféricos.
- **LiveView**: o suporte ao LiveView é muito recente. Você descobrirá as coisas à medida que avança. Felizmente, nada disso é muito difícil e qualquer trabalho poderá ser enviado `upstream` no Dioxus.
## Recursos
---
| Recurso | Situação | Descrição |
| ----------------------------------- | -------- | ------------------------------------------------------------------------------------------------ |
| Renderização Condicional | ✅ | `if/then` para esconder/mostrar componente |
| Map, Iterador | ✅ | `map/filter/reduce` para produzir `rsx!` |
| Componentes Chaveados | ✅ | comparação em `diff` com chaves |
| Web | ✅ | renderizador para navegadores Web |
| Desktop (WebView) | ✅ | renderizador para Desktop |
| Estado Compartilhado (Context) | ✅ | compartilha estados através de árvores |
| Hooks | ✅ | células de memoria nos componentes |
| SSR | ✅ | renderiza diretamente para `string` |
| Componente Filho | ✅ | `cx.children()` como lista de nós |
| Componentes Sem Elementos | ✅ | componentes que não renderizam elementos reais na DOM |
| Fragmentos | ✅ | elementos múltiplos sem uma raiz real |
| Propriedades Manuais | ✅ | passa manualmente `props` com a sintaxe de propagação (`spread syntax`) |
| Entradas Controladas | ✅ | encapsulamento com estado em sobre entradas |
| Estilos CSS/Inline | ✅ | sintaxe para grupos de estilo/atributos em linha |
| Elementos Personalizados | ✅ | define novos elementos primitivos |
| Suspensão | ✅ | programa futuras renderizações usando `future`/`Promise` |
| Tratamento Integrado de Erros | ✅ | trata erros graciosamente com a sintaxe `?` |
| NodeRef | ✅ | ganha acesso direto aos nós |
| Re-hidratação | ✅ | pre-renderiza HTML para acelerar a primeira impressão na tela |
| Renderização Livre de Gargalos | ✅ | `diffs` grandes são segmentados sobre quadros para uma transição suave como seda |
| Efeitos | ✅ | executa efeitos após um componente ser enviado para a fila de renderização |
| Portais | 🛠 | renderiza nós fora da árvore tradicional de elementos (DOM) |
| Agendamento Cooperativo | 🛠 | prioriza eventos com mais importância sobre eventos menos importantes |
| Componentes de Servidor | 🛠 | componentes híbridos para aplicativos de única página (SPA) e servidores |
| Divisão de Pacotes | 👀 | carrega o aplicativo assincronamente e eficientemente |
| Componentes Tardios | 👀 | dinamicamente carrega os novos componentes assim que estiverem prontos enquanto a página carrega |
| Estado Global de 1ª Classe | ✅ | `redux/recoil/mobx` sobre o `context` |
| Execução Nativa | ✅ | execução como um binário portátil sem um `runtime` (Node) |
| Sub-Árvore de Memoization | ✅ | pula o `diffing` em sub-árvores de elementos estáticos |
| Modelos de Alta Eficiência | 🛠 | chamadas `rsx!` são traduzidas para modelos sobre a `DOM` |
| Garantia de Correção por Compilador | ✅ | avisa sobre erros em esquemas de modelos inválidos antes do final da compilação |
| Motor de Heurística | ✅ | rastreia componentes na memória para minimizar alocações futures |
| Controle Preciso de Reatividade | 👀 | pula o `diffing` para ter controle preciso das atualizações de tela |
- ✅ = implementado e funcionando
- 🛠 = sendo trabalhado ativamente
- 👀 = ainda não implementado ou sendo trabalhado
## Roteiro
Esses recursos estão planejados para o futuro do Dioxus:
### Essencial
- [x] Liberação do Núcleo Dioxus
- [x] Atualizar a documentação para incluir mais teoria e ser mais abrangente
- [ ] Suporte para modelos HTML para manipulação de DOM ultrarrápida
- [ ] Suporte para vários renderizadores para o mesmo `virtualdom` (subárvores)
- [ ] Suporte para `ThreadSafe` (`Send` + `Sync`)
- [ ] Suporte para `Portals`
### SSR
- [x] Suporte SSR + Hidratação
- [ ] Suporte integrado de suspensão para SSR
### Desktop
- [ ] Gerenciamento de janela declarativa
- [ ] Modelos para construção/agregação
- [ ] Renderizador totalmente nativo
- [ ] Acesso ao contexto Canvas/WebGL nativamente
### Móvel
- [ ] Biblioteca padrão móvel
- [ ] GPS
- [ ] Câmera
- [ ] Sistema de Arquivo
- [ ] Biometria
- [ ] Wi-fi
- [ ] Bluetooth
- [ ] Notificações
- [ ] Prancheta (_Clipboard_)
- [ ] Animações
- [ ] Renderizador nativo
### Empacotamento (CLI)
- [x] Tradução de HTML para RSX
- [x] Servidor de desenvolvimento
- [x] Recarregamento em tempo-real (_hot-reload_)
- [x] Tradução de JSX para RSX
- [ ] Substituição de módulos em tempo-real (_hot-modules_)
- [ ] Divisão de código
- [ ] Acervo de macros
- [ ] _Pipeline_ CSS
- [ ] _Pipeline_ de imagens
### Hooks Essenciais
- [x] Roteador
- [x] Gerenciamento de estado global
- [ ] Redimensionar o observador
## Trabalho em Progresso
### Ferramenta de Construção
Atualmente, estamos trabalhando em nossa própria ferramenta de compilação chamada [Dioxus CLI](https://github.com/DioxusLabs/cli) que suportará:
- uma TUI interativa
- reconfiguração em tempo real
- recarga de CSS em tempo-real
- ligação de dados bidirecional entre o navegador e o código-fonte
- um interpretador para `rsx!`
- capacidade de publicar no github/netlify/vercel
- pacote para iOS/Desktop/etc
### Suporte ao LiveView / Componente do Servidor
A arquitetura interna do Dioxus foi projetada desde o primeiro dia para suportar o caso de uso `LiveView`, onde um servidor Web hospeda um aplicativo em execução para cada usuário conectado. A partir de hoje, não há suporte LiveView de primeira classe você precisará conectar isso sozinho.
Embora não esteja totalmente implementado, a expectativa é que os aplicativos LiveView possam ser um híbrido entre Wasm e renderizado pelo servidor, onde apenas partes de uma página são "ao vivo" e o restante da página é renderizado pelo servidor, gerado estaticamente ou manipulado pelo SPA anfitrião.

View file

@ -0,0 +1,25 @@
# Dioxus: Guias Avançados e Referência
![dioxuslogo](./images/dioxuslogo_full.png)
**Dioxus** é um framework e ecossistema para desenvolver interfaces rápidas, escaláveis e robustas com a linguagem de Programação Rust. Este guia irá ajudar você a começar com o Dioxus para Web, Desktop, Móvel e mais.
> Este livro é a Referência e Guias Avançados para o framework Dioxus. Para um tutorial em como de fato _usar_ o Dioxus, procure o [guia oficial](https://dioxuslabs.com/guide/).
## Guias e Referência
Com a referência nós procuramos manter a documentar a funcionalidade que pode não ter sido mencionada no guia oficial para manter uma carga de informação mínima. Alguns tópicos não estão inclusos pelo guia, mas discutidos nesta referência incluindo:
- Processo seguro (`ThreadSafe`) da `VirtualDOM`
- Abordagem complete sobre o uso do `rsx!` e funções inclusas
- Padrão `spread` para as propriedades dos componentes
- Testes
- Memoization à fundo
- Elementos personalizados
- Renderizadores personalizados
## Contribuindo
Se nesse documento estiver de algum forma confuso, contém erros de digitação ou você gostaria de ajudar a melhorar algo, sinta-se à vontade para fazer um PR no [repositório do Dioxus](https://github.com/DioxusLabs/dioxus/tree/master/docs/reference).
Todas as contribuições serão licenciadas sob a licença MIT/Apache2.

View file

@ -0,0 +1,47 @@
# Summary
- [Introdução](README.md)
- [Platformas](platforms/index.md)
- [Web](platforms/web.md)
- [Renderização por Servidor(SSR)](platforms/ssr.md)
- [Desktop](platforms/desktop.md)
- [Móvel](platforms/mobile.md)
- [TUI](platforms/tui.md)
- [Guias Avançados](guide/index.md)
- [RSX à fundo](guide/rsx_in_depth.md)
- [Componentes](guide/components.md)
- [Propriedades](guide/props.md)
- [Memoization](guide/memoization.md)
- [Desempenho](guide/performance.md)
- [Testes](guide/testing.md)
- [Construindo Elementos com o NodeFactory](guide/rsx.md)
- [Elementos Personalizados](guide/custom_elements.md)
- [Renderizadores Personalizados](guide/custom_renderer.md)
- [Componentes Renderizados por Servidor](guide/server_side_components.md)
- [Empacotando e Distribuindo](guide/bundline.md)
- [Recarregamento em Tempo-Real com RSX](guide/hot_reloading.md)
- [Guia de Referência](reference/reference.md)
- [Anti-padrões](reference/anti.md)
- [Filhos](reference/children.md)
- [Renderização Condicional](reference/conditional.md)
- [Entradas Controladas](reference/controlled.md)
- [Elementos Personalizados](reference/custom.md)
- [Componentes Vazios](reference/empty.md)
- [Tratamento de Errors](reference/error.md)
- [Fragmentos](reference/fragments.md)
- [CSS Globais](reference/global.md)
- [Estilos em Linha](reference/inline.md)
- [Iteradores](reference/iterators.md)
- [Ouvintes](reference/listeners.md)
- [Memoization](reference/memoization.md)
- [Nós de Referência](reference/node.md)
- [Padrão Propagado (Spread)](reference/spread.md)
- [Gerenciamento de Estado](reference/state.md)
- [Suspensão](reference/suspense.md)
- [Tarefas](reference/task.md)
- [Testes](reference/testing.md)

View file

@ -0,0 +1,497 @@
# Renderizador Personalizado
Dioxus é uma estrutura incrivelmente portátil para desenvolvimento de interface do usuário. As lições, conhecimentos, hooks e componentes que você adquire ao longo do tempo sempre podem ser aproveitados para projetos futuros. No entanto, às vezes, esses projetos não podem aproveitar um renderizador compatível ou você precisa implementar seu próprio renderizador melhor.
Ótimas notícias: o design do renderizador depende inteiramente de você! Nós fornecemos sugestões e inspiração com os renderizadores originais, mas só realmente precisamos processar `DomEdits` e enviar `UserEvents`.
## Em Detalhes:
A implementação do renderizador é bastante simples. O renderizador precisa:
1. Lidar com o fluxo de edições gerado por atualizações no DOM virtual
2. Registrar ouvintes e passe eventos para o sistema de eventos do DOM virtual
Essencialmente, seu renderizador precisa implementar o traço `RealDom` e gerar objetos `EventTrigger` para atualizar o `VirtualDOM`. A partir daí, você terá tudo o que precisa para renderizar o `VirtualDOM` na tela.
Internamente, o Dioxus lida com o relacionamento da árvore, `diffing`, gerenciamento de memória e o sistema de eventos, deixando o mínimo necessário para que os renderizadores se implementem.
Para referência, confira o interpretador JavaScript ou o renderizador TUI como ponto de partida para seu renderizador personalizado.
## DomEdições
O tipo "DomEdit" é uma enumeração serializada que representa uma operação atômica que ocorre no `RealDom`. As variantes seguem aproximadamente este conjunto:
```rust
enum DomEdit {
PushRoot,
AppendChildren,
ReplaceWith,
InsertAfter,
InsertBefore,
Remove,
CreateTextNode,
CreateElement,
CreateElementNs,
CreatePlaceholder,
NewEventListener,
RemoveEventListener,
SetText,
SetAttribute,
RemoveAttribute,
PopRoot,
}
```
O mecanismo de diferenciação do Dioxus opera como uma [máquina de pilha] (https://en.wikipedia.org/wiki/Stack_machine) onde o método `push_root` empilhar um novo nó DOM "real" para a pilha e `append_child` e `replace_with` ambos removem nós da pilha.
### Um exemplo
Por uma questão de compreensão, vamos considerar este exemplo - uma declaração de interface do usuário muito simples:
```rust
rsx!( h1 {"hello world"} )
```
To get things started, Dioxus must first navigate to the container of this h1 tag. To "navigate" here, the internal diffing algorithm generates the DomEdit `PushRoot` where the ID of the root is the container.
When the renderer receives this instruction, it pushes the actual Node onto its own stack. The real renderer's stack will look like this:
```rust
instructions: [
PushRoot(Container)
]
stack: [
ContainerNode,
]
```
Em seguida, o Dioxus encontrará o nó `h1`. O algoritmo `diff` decide que este nó precisa ser criado, então o Dioxus irá gerar o DomEdit `CreateElement`. Quando o renderizador receber esta instrução, ele criará um nó desmontado e o enviará para sua própria pilha:
```rust
instructions: [
PushRoot(Container),
CreateElement(h1),
]
stack: [
ContainerNode,
h1,
]
```
Em seguida, Dioxus vê o nó de texto e gera o DomEdit `CreateTextNode`:
```rust
instructions: [
PushRoot(Container),
CreateElement(h1),
CreateTextNode("hello world")
]
stack: [
ContainerNode,
h1,
"hello world"
]
```
Lembre-se, o nó de texto não está anexado a nada (ele está desmontado), então o Dioxus precisa gerar um `Edit` que conecte o nó de texto ao elemento `h1`. Depende da situação, mas neste caso usamos `AppendChildren`. Isso remove o nó de texto da pilha, deixando o elemento `h1` como o próximo elemento na linha.
```rust
instructions: [
PushRoot(Container),
CreateElement(h1),
CreateTextNode("hello world"),
AppendChildren(1)
]
stack: [
ContainerNode,
h1
]
```
Chamamos `AppendChildren` novamente, retirando o nó `h1` e anexando-o ao pai:
```rust
instructions: [
PushRoot(Container),
CreateElement(h1),
CreateTextNode("hello world"),
AppendChildren(1),
AppendChildren(1)
]
stack: [
ContainerNode,
]
```
Finalmente, o contêiner é aberto, pois não precisamos mais dele.
```rust
instructions: [
PushRoot(Container),
CreateElement(h1),
CreateTextNode("hello world"),
AppendChildren(1),
AppendChildren(1),
PopRoot
]
stack: []
```
Com o tempo, nossa pilha ficou assim:
```rust
[]
[Container]
[Container, h1]
[Container, h1, "hello world"]
[Container, h1]
[Container]
[]
```
Observe como nossa pilha fica vazia depois que a interface do usuário é montada. Convenientemente, essa abordagem separa completamente o `VirtualDOM` e o `RealDOM`. Além disso, essas edições são serializáveis, o que significa que podemos até gerenciar UIs em uma conexão de rede. Esta pequena máquina de pilha e edições serializadas tornam o Dioxus independente das especificidades da plataforma.
Dioxus também é muito rápido. Como o Dioxus divide a fase de `diff` e `patch`, ele é capaz de fazer todas as edições no `RealDOM` em um período de tempo muito curto (menos de um único quadro), tornando a renderização muito rápida. Ele também permite que o Dioxus cancele grandes operações de diferenciação se ocorrer um trabalho de prioridade mais alta durante a diferenciação.
É importante notar que há uma camada de conexão entre o Dioxus e o renderizador. O Dioxus salva e carrega elementos (a edição `PushRoot`) com um ID. Dentro do `VirtualDOM`, isso é rastreado apenas como um `u64`.
Sempre que uma edição `CreateElement` é gerada durante a comparação, o Dioxus incrementa seu contador de nós e atribui a esse novo elemento seu `NodeCount` atual. O `RealDom` é responsável por lembrar este ID e enviar o nó correto quando `PushRoot(ID)` é gerado. Dioxus recupera os IDs de elementos quando removidos. Para ficar em sincronia com Dioxus, você pode usar um `Sparce Vec` (`Vec<Option<T>>`) com itens possivelmente desocupados. Você pode usar os ids como índices no `Vec` para elementos e aumentar o `Vec` quando um id não existir.
Esta pequena demonstração serve para mostrar exatamente como um renderizador precisaria processar um stream de edição para construir UIs. Um conjunto de DOMEdits serializados para várias demos está disponível para você testar seu renderizador personalizado.
## Ciclo de eventos
Como a maioria das GUIs, o Dioxus conta com um `loop` de eventos para progredir no `VirtualDOM`. O próprio `VirtualDOM` também pode produzir eventos, por isso é importante que seu renderizador personalizado também possa lidar com eles.
O código para a implementação do `WebSys` é direto, então vamos adicioná-lo aqui para demonstrar como um `loop` de eventos é simples:
```rust
pub async fn run(&mut self) -> dioxus_core::error::Result<()> {
// Push the body element onto the WebsysDom's stack machine
let mut websys_dom = crate::new::WebsysDom::new(prepare_websys_dom());
websys_dom.stack.push(root_node);
// Rebuild or hydrate the virtualdom
let mutations = self.internal_dom.rebuild();
websys_dom.apply_mutations(mutations);
// Wait for updates from the real dom and progress the virtual dom
loop {
let user_input_future = websys_dom.wait_for_event();
let internal_event_future = self.internal_dom.wait_for_work();
match select(user_input_future, internal_event_future).await {
Either::Left((_, _)) => {
let mutations = self.internal_dom.work_with_deadline(|| false);
websys_dom.apply_mutations(mutations);
},
Either::Right((event, _)) => websys_dom.handle_event(event),
}
// render
}
}
```
É importante que você decodifique os eventos reais do seu sistema de eventos no sistema de eventos sintético do Dioxus (significado sintético abstraído). Isso significa simplesmente combinar seu tipo de evento e criar um tipo Dioxus `UserEvent`. No momento, o sistema `VirtualEvent` é modelado quase inteiramente em torno da especificação HTML, mas estamos interessados em reduzi-lo.
```rust
fn virtual_event_from_websys_event(event: &web_sys::Event) -> VirtualEvent {
match event.type_().as_str() {
"keydown" => {
let event: web_sys::KeyboardEvent = event.clone().dyn_into().unwrap();
UserEvent::KeyboardEvent(UserEvent {
scope_id: None,
priority: EventPriority::Medium,
name: "keydown",
// This should be whatever element is focused
element: Some(ElementId(0)),
data: Arc::new(KeyboardData{
char_code: event.char_code(),
key: event.key(),
key_code: event.key_code(),
alt_key: event.alt_key(),
ctrl_key: event.ctrl_key(),
meta_key: event.meta_key(),
shift_key: event.shift_key(),
locale: "".to_string(),
location: event.location(),
repeat: event.repeat(),
which: event.which(),
})
})
}
_ => todo!()
}
}
```
## Elementos brutos personalizados
Se você precisar ir mais longe a ponto de confiar em elementos personalizados para o seu renderizador - você pode. Isso ainda permitiria que você usasse a natureza reativa do Dioxus, sistema de componentes, estado compartilhado e outros recursos, mas acabará gerando nós diferentes. Todos os atributos e ouvintes para o namespace HTML e SVG são transportados por meio de estruturas auxiliares que essencialmente compilam (não representam sobrecarga de tempo de execução). Você pode colocar seus próprios elementos a qualquer hora que quiser, sem problemas. No entanto, você deve ter certeza absoluta de que seu renderizador pode lidar com o novo tipo, ou ele irá "bater e queimar".
Esses elementos personalizados são definidos como `unit struct` com implementações de `traits`.
Por exemplo, o elemento `div` é (aproximadamente!) definido assim:
```rust
struct div;
impl div {
/// Some glorious documentation about the class property.
const TAG_NAME: &'static str = "div";
const NAME_SPACE: Option<&'static str> = None;
// define the class attribute
pub fn class<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
cx.attr("class", val, None, false)
}
// more attributes
}
```
Você provavelmente notou que muitos elementos nas macros `rsx!` suportam documentação em foco. A abordagem que adotamos para elementos personalizados significa que a estrutura da unidade é criada imediatamente onde o elemento é usado no macro. Quando o macro é expandido, os comentários doc ainda se aplicam à estrutura da unidade, dando toneladas de feedback no editor, mesmo dentro de uma macro procedural.
# Núcleo Nativo
Os renderizadores dão muito trabalho. Se você estiver criando um renderizador em Rust, o núcleo nativo fornece alguns utilitários para implementar um renderizador. Ele fornece uma abstração sobre DomEdits e manipula o layout para você.
## RealDom
O `RealDom` é uma abstração de nível superior sobre a atualização do DOM. Ele é atualizado com `DomEdits` e fornece uma maneira de atualizar lentamente o estado dos nós com base em quais atributos mudam.
### Exemplo
Vamos construir um renderizador de brinquedo com bordas, tamanho e cor do texto.
Antes de começarmos, vamos dar uma olhada em um elemento de exemplo que podemos renderizar:
```rust
cx.render(rsx!{
div{
color: "red",
p{
border: "1px solid black",
"hello world"
}
}
})
```
Nesta árvore a cor depende da cor do pai. O tamanho depende do tamanho das crianças, do texto atual e do tamanho do texto. A borda depende apenas do nó atual.
```mermaid
flowchart TB
subgraph context
text_width(text width)
end
subgraph div
state1(state)-->color1(color)
state1(state)-->border1(border)
border1-.->text_width
linkStyle 2 stroke:#5555ff,stroke-width:4px;
state1(state)-->layout_width1(layout width)
end
subgraph p
state2(state)-->color2(color)
color2-.->color1(color)
linkStyle 5 stroke:#0000ff,stroke-width:4px;
state2(state)-->border2(border)
border2-.->text_width
linkStyle 7 stroke:#5555ff,stroke-width:4px;
state2(state)-->layout_width2(layout width)
layout_width1-.->layout_width2
linkStyle 9 stroke:#aaaaff,stroke-width:4px;
end
subgraph hello world
state3(state)-->color3(color)
color3-.->color2(color)
linkStyle 11 stroke:#0000ff,stroke-width:4px;
state3(state)-->border3(border)
border3-.->text_width
linkStyle 13 stroke:#5555ff,stroke-width:4px;
state3(state)-->layout_width3(layout width)
layout_width2-.->layout_width3
linkStyle 15 stroke:#aaaaff,stroke-width:4px;
end
```
Para ajudar na construção de um Dom, o núcleo nativo fornece quatro características: `State`, `ChildDepState`, `ParentDepState` e `NodeDepState` e uma estrutura `RealDom`.
```rust
use dioxus_native_core::node_ref::*;
use dioxus_native_core::state::{ChildDepState, NodeDepState, ParentDepState, State};
use dioxus_native_core_macro::{sorted_str_slice, State};
#[derive(Default, Copy, Clone)]
struct Size(f32, f32);
// Size only depends on the current node and its children, so it implements ChildDepState
impl ChildDepState for Size {
// Size accepts a font size context
type Ctx = f32;
// Size depends on the Size part of each child
type DepState = Self;
// Size only cares about the width, height, and text parts of the current node
const NODE_MASK: NodeMask =
NodeMask::new_with_attrs(AttributeMask::Static(&sorted_str_slice!(["width", "height"]))).with_text();
fn reduce<'a>(
&mut self,
node: NodeView,
children: impl Iterator<Item = &'a Self::DepState>,
ctx: &Self::Ctx,
) -> bool
where
Self::DepState: 'a,
{
let mut width;
let mut height;
if let Some(text) = node.text() {
// if the node has text, use the text to size our object
width = text.len() as f32 * ctx;
height = ctx;
} else {
// otherwise, the size is the maximum size of the children
width = *children
.reduce(|accum, item| if accum >= item.0 { accum } else { item.0 })
.unwrap_or(0.0));
height = *children
.reduce(|accum, item| if accum >= item.1 { accum } else { item.1 })
.unwrap_or(&0.0);
}
// if the node contains a width or height attribute it overrides the other size
for a in node.attibutes(){
match a.name{
"width" => width = a.value.parse().unwrap(),
"height" => height = a.value.parse().unwrap(),
// because Size only depends on the width and height, no other attributes will be passed to the member
_ => panic!()
}
}
// to determine what other parts of the dom need to be updated we return a boolean that marks if this member changed
let changed = (width != self.0) || (height != self.1);
*self = Self(width, height);
changed
}
}
#[derive(Debug, Clone, Copy, PartialEq, Default)]
struct TextColor {
r: u8,
g: u8,
b: u8,
}
// TextColor only depends on the current node and its parent, so it implements ParentDepState
impl ParentDepState for TextColor {
type Ctx = ();
// TextColor depends on the TextColor part of the parent
type DepState = Self;
// TextColor only cares about the color attribute of the current node
const NODE_MASK: NodeMask = NodeMask::new_with_attrs(AttributeMask::Static(&["color"]));
fn reduce(
&mut self,
node: NodeView,
parent: Option<&Self::DepState>,
_ctx: &Self::Ctx,
) -> bool {
// TextColor only depends on the color tag, so getting the first tag is equivilent to looking through all tags
let new = match node.attributes().next() {
// if there is a color tag, translate it
Some("red") => TextColor { r: 255, g: 0, b: 0 },
Some("green") => TextColor { r: 0, g: 255, b: 0 },
Some("blue") => TextColor { r: 0, g: 0, b: 255 },
Some(_) => panic!("unknown color"),
// otherwise check if the node has a parent and inherit that color
None => match parent {
Some(parent) => *parent,
None => Self::default(),
},
};
// check if the member has changed
let changed = new != *self;
*self = new;
changed
}
}
#[derive(Debug, Clone, PartialEq, Default)]
struct Border(bool);
// TextColor only depends on the current node, so it implements NodeDepState
impl NodeDepState for Border {
type Ctx = ();
// Border does not depended on any other member in the current node
type DepState = ();
// Border does not depended on any other member in the current node
const NODE_MASK: NodeMask =
NodeMask::new_with_attrs(AttributeMask::Static(&["border"]));
fn reduce(&mut self, node: NodeView, _sibling: &Self::DepState, _ctx: &Self::Ctx) -> bool {
// check if the node contians a border attribute
let new = Self(node.attributes().next().map(|a| a.name == "border").is_some());
// check if the member has changed
let changed = new != *self;
*self = new;
changed
}
}
// State provides a derive macro, but anotations on the members are needed in the form #[dep_type(dep_member, CtxType)]
#[derive(State, Default, Clone)]
struct ToyState {
// the color member of it's parent and no context
#[parent_dep_state(color)]
color: TextColor,
// depends on the node, and no context
#[node_dep_state()]
border: Border,
// depends on the layout_width member of children and f32 context (for text size)
#[child_dep_state(size, f32)]
size: Size,
}
```
Agora que temos nosso estado, podemos colocá-lo em uso em nosso DOM. Nós podemos atualizar o DOM com `update_state` para atualizar a estrutura do `DOM` (adicionando, removendo e alterando as propriedades dos nós) e então `apply_mutations` para atualizar o `ToyState` para cada um dos nós que foram alterados.
```rust
fn main(){
fn app(cx: Scope) -> Element {
cx.render(rsx!{
div{
color: "red",
"hello world"
}
})
}
let vdom = VirtualDom::new(app);
let rdom: RealDom<ToyState> = RealDom::new();
let mutations = dom.rebuild();
// update the structure of the real_dom tree
let to_update = rdom.apply_mutations(vec![mutations]);
let mut ctx = AnyMap::new();
// set the font size to 3.3
ctx.insert(3.3);
// update the ToyState for nodes in the real_dom tree
let _to_rerender = rdom.update_state(&dom, to_update, ctx).unwrap();
// we need to run the vdom in a async runtime
tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()?
.block_on(async {
loop{
let wait = vdom.wait_for_work();
let mutations = vdom.work_with_deadline(|| false);
let to_update = rdom.apply_mutations(mutations);
let mut ctx = AnyMap::new();
ctx.insert(3.3);
let _to_rerender = rdom.update_state(vdom, to_update, ctx).unwrap();
// render...
}
})
}
```
## Disposição
Para a maioria das plataformas, o layout dos Elementos permanecerá o mesmo. O módulo layout_attributes fornece uma maneira de aplicar atributos `html` a um estilo de layout estendido.
## Conclusão
Pronto, é isso! Você deve ter quase todo o conhecimento necessário sobre como implementar seu próprio renderizador. Estamos super interessados em ver os aplicativos Dioxus trazidos para renderizadores de desktop personalizados, renderizador para dispositivos móveis, interface do usuário para videogames e até realidade aumentada! Se você estiver interessado em contribuir para qualquer um desses projetos, não tenha medo de entrar em contato ou se juntar à comunidade.

View file

@ -0,0 +1,31 @@
# Recarregamento a Quente
1. O recarregamento a quente permite tempos de iteração muito mais rápidos dentro de chamadas rsx, interpretando-as e transmitindo as edições.
2. É útil ao alterar o estilo/layout de um programa, mas não ajudará na alteração da lógica de um programa.
3. Atualmente, o cli implementa apenas o recarregamento a quente para o renderizador da web.
# Configurar
Instale o [dioxus-cli](https://github.com/DioxusLabs/cli).
Habilite o recurso hot_reload no dioxus:
```toml
dioxus = { version = "*", features = ["web", "hot_reload"] }
```
# Como Usar
1. run:
```
dioxus serve --hot-reload
```
2. alterar algum código dentro de uma macro rsx
3. abra seu localhost em um navegador
4. salve e observe a mudança de estilo sem recompilar
# Limitações
1. O intérprete só pode usar expressões que existiam na última recompilação completa. Se você introduzir uma nova variável ou expressão na chamada rsx, ela acionará uma recompilação completa para capturar a expressão.
2. Componentes e Iteradores podem conter código de Rust arbitrário e acionarão uma recompilação completa quando alterados.

View file

@ -0,0 +1 @@
# Guias Avançados

View file

@ -0,0 +1,228 @@
# RSX in Depth
The RSX macro makes it very easy to assemble complex UIs with a very natural Rust syntax:
```rust
rsx!(
div {
button {
onclick: move |e| todos.write().new_todo(),
"Add todo"
}
ul {
class: "todo-list",
todos.iter().map(|(key, todo)| rsx!(
li {
class: "beautiful-todo"
key: "f"
h3 { "{todo.title}" }
p { "{todo.contents}"}
}
))
}
}
)
```
In this section, we'll cover the `rsx!` macro in depth. If you prefer to learn through examples, the `code reference` guide has plenty of examples on how to use `rsx!` effectively.
### Element structure
Attributes must come before child elements
```rust
div {
hidden: "false",
"some text"
child {}
Component {} // uppercase
component() // lowercase is treated like a function call
(0..10).map(|f| rsx!{ "hi {f}" }) // arbitrary rust expressions
}
```
Each element takes a comma-separated list of expressions to build the node. Roughly, here's how they work:
- `name: value` sets a property on this element.
- `"text"` adds a new text element
- `tag {}` adds a new child element
- `CustomTag {}` adds a new child component
- `{expr}` pastes the `expr` tokens literally. They must be `IntoIterator<T> where T: IntoVnode` to work properly
Commas are entirely optional, but might be useful to delineate between elements and attributes.
The `render` function provides an **extremely efficient** allocator for VNodes and text, so try not to use the `format!` macro in your components. Rust's default `ToString` methods pass through the global allocator, but all text in components is allocated inside a manually-managed Bump arena. To push you in the right direction, all text-based attributes take `std::fmt::Arguments` directly, so you'll want to reach for `format_args!` when the built-in `f-string` interpolation just doesn't cut it.
### Ignoring `cx.render` with `rsx!(cx, ...)`
Sometimes, writing `cx.render` is a hassle. The `rsx! macro will accept any token followed by a comma as the target to call "render" on:
```rust
cx.render(rsx!( div {} ))
// becomes
rsx!(cx, div {})
```
### Conditional Rendering
Sometimes, you might not want to render an element given a condition. The rsx! macro will accept any tokens directly contained with curly braces, provided they resolve to a type that implements `IntoIterator<VNode>`. This lets us write any Rust expression that resolves to a VNode:
```rust
rsx!({
if enabled {
rsx!(cx, div {"enabled"})
} else {
rsx!(cx, li {"disabled"})
}
})
```
A convenient way of hiding/showing an element is returning an `Option<VNode>`. When combined with `and_then`, we can succinctly control the display state given some boolean:
```rust
rsx!({
a.and_then(rsx!(div {"enabled"}))
})
```
It's important to note that the expression `rsx!()` is typically lazy - this expression must be _rendered_ to produce a VNode. When using match statements, we must render every arm as to avoid the `no two closures are identical` rule that Rust imposes:
```rust
// this will not compile!
match case {
true => rsx!(div {}),
false => rsx!(div {})
}
// the nodes must be rendered first
match case {
true => rsx!(cx, div {}),
false => rsx!(cx, div {})
}
```
### Lists
Again, because anything that implements `IntoIterator<VNode>` is valid, we can use lists directly in our `rsx!`:
```rust
let items = vec!["a", "b", "c"];
cx.render(rsx!{
ul {
{items.iter().map(|f| rsx!(li { "a" }))}
}
})
```
Sometimes, it makes sense to render VNodes into a list:
```rust
let mut items = vec![];
for _ in 0..5 {
items.push(rsx!(cx, li {} ))
}
rsx!(cx, {items} )
```
#### Lists and Keys
When rendering the VirtualDom to the screen, Dioxus needs to know which elements have been added and which have been removed. These changes are determined through a process called "diffing" - an old set of elements is compared to a new set of elements. If an element is removed, then it won't show up in the new elements, and Dioxus knows to remove it.
However, with lists, Dioxus does not exactly know how to determine which elements have been added or removed if the order changes or if an element is added or removed from the middle of the list.
In these cases, it is vitally important to specify a "key" alongside the element. Keys should be persistent between renders.
```rust
fn render_list(cx: Scope, items: HashMap<String, Todo>) -> DomTree {
rsx!(cx, ul {
{items.iter().map(|key, item| {
li {
key: key,
h2 { "{todo.title}" }
p { "{todo.contents}" }
}
})}
})
}
```
There have been many guides made for keys in React, so we recommend reading up to understand their importance:
- [React guide on keys](https://reactjs.org/docs/lists-and-keys.html)
- [Importance of keys (Medium)](https://kentcdodds.com/blog/understanding-reacts-key-prop)
### Complete Reference
```rust
let text = "example";
cx.render(rsx!{
div {
h1 { "Example" },
{title}
// fstring interpolation
"{text}"
p {
// Attributes
tag: "type",
// Anything that implements display can be an attribute
abc: 123,
enabled: true,
// attributes also supports interpolation
// `class` is not a restricted keyword unlike JS and ClassName
class: "big small wide short {text}",
class: format_args!("attributes take fmt::Arguments. {}", 99),
tag: {"these tokens are placed directly"}
// Children
a { "abcder" },
// Children with attributes
h2 { "hello", class: "abc-123" },
// Child components
CustomComponent { a: 123, b: 456, key: "1" },
// Child components with paths
crate::components::CustomComponent { a: 123, b: 456, key: "1" },
// Iterators
{ (0..3).map(|i| rsx!( h1 {"{:i}"} )) },
// More rsx!, or even html!
{ rsx! { div { } } },
{ html! { <div> </div> } },
// Matching
// Requires rendering the nodes first.
// rsx! is lazy, and the underlying closures cannot have the same type
// Rendering produces the VNode type
{match rand::gen_range::<i32>(1..3) {
1 => rsx!(cx, h1 { "big" })
2 => rsx!(cx, h2 { "medium" })
_ => rsx!(cx, h3 { "small" })
}}
// Optionals
{true.and_then(|f| rsx!( h1 {"Conditional Rendering"} ))}
// Child nodes
{cx.props.children}
// Any expression that is `IntoVNode`
{expr}
}
}
})
```

View file

@ -0,0 +1,228 @@
# RSX à Fundo
A macro RSX facilita muito a montagem de interfaces de usuário complexas com uma sintaxe Rust muito natural:
```rust
rsx!(
div {
button {
onclick: move |e| todos.write().new_todo(),
"Add todo"
}
ul {
class: "todo-list",
todos.iter().map(|(key, todo)| rsx!(
li {
class: "beautiful-todo"
key: "f"
h3 { "{todo.title}" }
p { "{todo.contents}"}
}
))
}
}
)
```
Nesta seção, abordaremos a macro `rsx!` em profundidade. Se você preferir aprender através de exemplos, o guia `referência de código` tem muitos exemplos sobre como usar `rsx!` efetivamente.
### Estrutura do elemento
Os atributos devem vir antes dos elementos filhos
```rust
div {
hidden: "false",
"some text"
child {}
Component {} // uppercase
component() // lowercase is treated like a function call
(0..10).map(|f| rsx!{ "hi {f}" }) // arbitrary rust expressions
}
```
Cada elemento usa uma lista de expressões separadas por vírgulas para construir o nó. A grosso modo, veja como eles funcionam:
- `name: value` define uma propriedade neste elemento.
- `text` adiciona um novo elemento de texto
- `tag {}` adiciona um novo elemento filho
- `CustomTag {}` adiciona um novo componente filho
- `{expr}` cola os tokens `expr` literalmente. Eles devem ser `IntoIterator<T> where T: IntoVnode` para funcionar corretamente
As vírgulas são totalmente opcionais, mas podem ser úteis para delinear entre elementos e atributos.
A função `render` fornece um alocador **extremamente eficiente** para `VNodes` e `text`, então tente não usar a macro `format!` em seus componentes. Os métodos `ToString` padrão do Rust passam pelo alocador global, mas todo o texto nos componentes é alocado dentro de uma ""arena Bump"" gerenciada manualmente. Para levá-lo na direção certa, todos os atributos baseados em texto recebem `std::fmt::Arguments` diretamente, então você vai querer usar `format_args!` quando a interpolação interna `f-string` simplesmente não funcionar.
### Ignorando `cx.render` com `rsx!(cx, ...)`
Às vezes, escrever `cx.render` é um aborrecimento. O `rsx!` macro aceitará qualquer token seguido por uma vírgula como destino para chamar "render" em:
```rust
cx.render(rsx!( div {} ))
// becomes
rsx!(cx, div {})
```
### Renderização Condicional
Às vezes, você pode não querer renderizar um elemento dada uma condição. O `rsx!` macro aceitará quaisquer tokens contidos diretamente com chaves, desde que resolvam para um tipo que implemente `IntoIterator<VNode>`. Isso nos permite escrever qualquer expressão Rust que resolva para um `VNode`:
```rust
rsx!({
if enabled {
rsx!(cx, div {"enabled"})
} else {
rsx!(cx, li {"disabled"})
}
})
```
Uma maneira conveniente de ocultar/mostrar um elemento é retornar um `Option<VNode>`. Quando combinado com `and_then`, podemos controlar sucintamente o estado de exibição dado alguns booleanos:
```rust
rsx!({
a.and_then(rsx!(div {"enabled"}))
})
```
É importante notar que a expressão `rsx!()` é tipicamente tardia - esta expressão deve ser _renderizada_ para produzir um `VNode`. Ao usar declarações de `match`, devemos renderizar todos os braços para evitar a regra 'não há dois fechamentos idênticos' que o Rust impõe:
```rust
// this will not compile!
match case {
true => rsx!(div {}),
false => rsx!(div {})
}
// the nodes must be rendered first
match case {
true => rsx!(cx, div {}),
false => rsx!(cx, div {})
}
```
### Listas
Novamente, porque qualquer coisa que implemente `IntoIterator<VNode>` é válida, podemos usar listas diretamente em nosso `rsx!`:
```rust
let items = vec!["a", "b", "c"];
cx.render(rsx!{
ul {
{items.iter().map(|f| rsx!(li { "a" }))}
}
})
```
Às vezes, faz sentido renderizar `VNodes` em uma lista:
```rust
let mut items = vec![];
for _ in 0..5 {
items.push(rsx!(cx, li {} ))
}
rsx!(cx, {items} )
```
#### Listas e chaves
Ao renderizar o `VirtualDom` na tela, o Dioxus precisa saber quais elementos foram adicionados e quais foram removidos. Essas mudanças são determinadas através de um processo chamado "diffing" - um antigo conjunto de elementos é comparado a um novo conjunto de elementos. Se um elemento for removido, ele não aparecerá nos novos elementos, e Dioxus sabe removê-lo.
No entanto, com listas, Dioxus não sabe exatamente como determinar quais elementos foram adicionados ou removidos se a ordem mudar ou se um elemento for adicionado ou removido do meio da lista.
Nesses casos, é de vital importância especificar uma "chave" ao lado do elemento. As chaves devem ser persistentes entre as renderizações.
```rust
fn render_list(cx: Scope, items: HashMap<String, Todo>) -> DomTree {
rsx!(cx, ul {
{items.iter().map(|key, item| {
li {
key: key,
h2 { "{todo.title}" }
p { "{todo.contents}" }
}
})}
})
}
```
Existem muitos guias feitos para chaves no React, então recomendamos a leitura para entender sua importância:
- [React guide on keys](https://reactjs.org/docs/lists-and-keys.html)
- [Importância das chaves (Média)](https://kentcdodds.com/blog/understanding-reacts-key-prop)
### Referência Completa
```rust
let text = "example";
cx.render(rsx!{
div {
h1 { "Example" },
{title}
// fstring interpolation
"{text}"
p {
// Attributes
tag: "type",
// Anything that implements display can be an attribute
abc: 123,
enabled: true,
// attributes also supports interpolation
// `class` is not a restricted keyword unlike JS and ClassName
class: "big small wide short {text}",
class: format_args!("attributes take fmt::Arguments. {}", 99),
tag: {"these tokens are placed directly"}
// Children
a { "abcder" },
// Children with attributes
h2 { "hello", class: "abc-123" },
// Child components
CustomComponent { a: 123, b: 456, key: "1" },
// Child components with paths
crate::components::CustomComponent { a: 123, b: 456, key: "1" },
// Iterators
{ (0..3).map(|i| rsx!( h1 {"{:i}"} )) },
// More rsx!, or even html!
{ rsx! { div { } } },
{ html! { <div> </div> } },
// Matching
// Requires rendering the nodes first.
// rsx! is lazy, and the underlying closures cannot have the same type
// Rendering produces the VNode type
{match rand::gen_range::<i32>(1..3) {
1 => rsx!(cx, h1 { "big" })
2 => rsx!(cx, h2 { "medium" })
_ => rsx!(cx, h3 { "small" })
}}
// Optionals
{true.and_then(|f| rsx!( h1 {"Conditional Rendering"} ))}
// Child nodes
{cx.props.children}
// Any expression that is `IntoVNode`
{expr}
}
}
})
```

View file

@ -0,0 +1,45 @@
# Getting Started: Desktop
One of Dioxus' killer features is the ability to quickly build a native desktop app that looks and feels the same across platforms. Apps built with Dioxus are typically <5mb in size and use existing system resources, so they won't hog extreme amounts of RAM or memory.
Dioxus Desktop is built off Tauri. Right now there aren't any Dioxus abstractions over keyboard shortcuts, menubar, handling, etc, so you'll want to leverage Tauri - mostly [Wry](http://github.com/tauri-apps/wry/) and [Tao](http://github.com/tauri-apps/tao)) directly. The next major release of Dioxus-Desktop will include components and hooks for notifications, global shortcuts, menubar, etc.
## Getting Set up
Getting Set up with Dioxus-Desktop is quite easy. Make sure you have Rust and Cargo installed, and then create a new project:
```shell
$ cargo new --bin demo
$ cd demo
```
Add Dioxus with the `desktop` feature:
```shell
$ cargo add dioxus --features desktop
```
Edit your `main.rs`:
```rust
// main.rs
use dioxus::prelude::*;
fn main() {
dioxus::desktop::launch(app);
}
fn app(cx: Scope) -> Element {
cx.render(rsx!{
div {
"hello world!"
}
})
}
```
To configure the webview, menubar, and other important desktop-specific features, checkout out some of the launch configuration in the [API reference](https://docs.rs/dioxus-desktop/).
## Future Steps
Make sure to read the [Dioxus Guide](https://dioxuslabs.com/guide) if you already haven't!

View file

@ -0,0 +1,11 @@
# Platforms
Dioxus supports many different platforms. Below are a list of guides that walk you through setting up Dioxus for a specific platform.
### Setup Guides
- [Web](web.md)
- [Server Side Rendering](ssr.md)
- [Desktop](desktop.md)
- [Mobile](mobile.md)
- [TUI](tui.md)

View file

@ -23,7 +23,7 @@ fn main() {
}
fn app(cx: Scope) -> Element {
let text = cx.use_hook(|_| vec![String::from("abc=def")]);
let text = cx.use_hook(|| vec![String::from("abc=def")]);
let first = text.get_mut(0).unwrap();

View file

@ -658,7 +658,7 @@ impl ScopeState {
/// struct SharedState(&'static str);
///
/// static App: Component = |cx| {
/// cx.use_hook(|_| cx.provide_context(SharedState("world")));
/// cx.use_hook(|| cx.provide_context(SharedState("world")));
/// rsx!(cx, Child {})
/// }
///
@ -684,7 +684,7 @@ impl ScopeState {
/// struct SharedState(&'static str);
///
/// static App: Component = |cx| {
/// cx.use_hook(|_| cx.provide_root_context(SharedState("world")));
/// cx.use_hook(|| cx.provide_root_context(SharedState("world")));
/// rsx!(cx, Child {})
/// }
///
@ -811,33 +811,31 @@ impl ScopeState {
}))
}
/// Store a value between renders
/// Store a value between renders. The foundational hook for all other hooks.
///
/// This is *the* foundational hook for all other hooks.
/// Accepts an `initializer` closure, which is run on the first use of the hook (typically the initial render). The return value of this closure is stored for the lifetime of the component, and a mutable reference to it is provided on every render as the return value of `use_hook`.
///
/// - Initializer: closure used to create the initial hook state
/// - Runner: closure used to output a value every time the hook is used
///
/// To "cleanup" the hook, implement `Drop` on the stored hook value. Whenever the component is dropped, the hook
/// will be dropped as well.
/// When the component is unmounted (removed from the UI), the value is dropped. This means you can return a custom type and provide cleanup code by implementing the [`Drop`] trait
///
/// # Example
///
/// ```ignore
/// // use_ref is the simplest way of storing a value between renders
/// fn use_ref<T: 'static>(initial_value: impl FnOnce() -> T) -> &RefCell<T> {
/// use_hook(|| Rc::new(RefCell::new(initial_value())))
/// ```
/// use dioxus_core::ScopeState;
///
/// // prints a greeting on the initial render
/// pub fn use_hello_world(cx: &ScopeState) {
/// cx.use_hook(|| println!("Hello, world!"));
/// }
/// ```
#[allow(clippy::mut_from_ref)]
pub fn use_hook<State: 'static>(&self, initializer: impl FnOnce(usize) -> State) -> &mut State {
pub fn use_hook<State: 'static>(&self, initializer: impl FnOnce() -> State) -> &mut State {
let mut vals = self.hook_vals.borrow_mut();
let hook_len = vals.len();
let cur_idx = self.hook_idx.get();
if cur_idx >= hook_len {
vals.push(self.hook_arena.alloc(initializer(hook_len)));
vals.push(self.hook_arena.alloc(initializer()));
}
vals

View file

@ -10,7 +10,7 @@ pub type ProxyType = EventLoopProxy<UserWindowEvent>;
/// Get an imperative handle to the current window
pub fn use_window(cx: &ScopeState) -> &DesktopContext {
cx.use_hook(|_| cx.consume_context::<DesktopContext>())
cx.use_hook(|| cx.consume_context::<DesktopContext>())
.as_ref()
.unwrap()
}
@ -232,5 +232,5 @@ pub(super) fn handler(
pub fn use_eval<S: std::string::ToString>(cx: &ScopeState) -> &dyn Fn(S) {
let desktop = use_window(cx).clone();
cx.use_hook(|_| move |script| desktop.eval(script))
cx.use_hook(|| move |script| desktop.eval(script))
}

View file

@ -8,7 +8,7 @@ fn test_borrowed_state() {
}
fn Parent(cx: Scope) -> Element {
let value = cx.use_hook(|_| String::new());
let value = cx.use_hook(|| String::new());
cx.render(rsx! {
div {

View file

@ -20,7 +20,7 @@ fn new_dom<P: 'static + Send>(app: Component<P>, props: P) -> VirtualDom {
#[test]
fn test_early_abort() {
const app: Component = |cx| {
let val = cx.use_hook(|_| 0);
let val = cx.use_hook(|| 0);
*val += 1;

View file

@ -34,7 +34,7 @@ fn manual_diffing() {
#[test]
fn events_generate() {
fn app(cx: Scope) -> Element {
let count = cx.use_hook(|_| 0);
let count = cx.use_hook(|| 0);
let inner = match *count {
0 => {
@ -77,7 +77,7 @@ fn events_generate() {
#[test]
fn components_generate() {
fn app(cx: Scope) -> Element {
let render_phase = cx.use_hook(|_| 0);
let render_phase = cx.use_hook(|| 0);
*render_phase += 1;
cx.render(match *render_phase {
@ -171,7 +171,7 @@ fn components_generate() {
#[test]
fn component_swap() {
fn app(cx: Scope) -> Element {
let render_phase = cx.use_hook(|_| 0);
let render_phase = cx.use_hook(|| 0);
*render_phase += 1;
cx.render(match *render_phase {

View file

@ -22,7 +22,7 @@ fn new_dom<P: 'static + Send>(app: Component<P>, props: P) -> VirtualDom {
#[test]
fn test_memory_leak() {
fn app(cx: Scope) -> Element {
let val = cx.use_hook(|_| 0);
let val = cx.use_hook(|| 0);
*val += 1;
@ -30,7 +30,7 @@ fn test_memory_leak() {
return None;
}
let name = cx.use_hook(|_| String::from("asd"));
let name = cx.use_hook(|| String::from("asd"));
cx.render(rsx!(
div { "Hello, world!" }
@ -79,7 +79,7 @@ fn test_memory_leak() {
#[test]
fn memo_works_properly() {
fn app(cx: Scope) -> Element {
let val = cx.use_hook(|_| 0);
let val = cx.use_hook(|| 0);
*val += 1;
@ -87,7 +87,7 @@ fn memo_works_properly() {
return None;
}
let name = cx.use_hook(|_| String::from("asd"));
let name = cx.use_hook(|| String::from("asd"));
cx.render(rsx!(
div { "Hello, world! {name}" }
@ -192,7 +192,7 @@ fn free_works_on_root_hooks() {
}
fn app(cx: Scope) -> Element {
let name = cx.use_hook(|_| Droppable(String::from("asd")));
let name = cx.use_hook(|| Droppable(String::from("asd")));
rsx!(cx, div { "{name.0}" })
}
@ -204,7 +204,7 @@ fn free_works_on_root_hooks() {
fn old_props_arent_stale() {
fn app(cx: Scope) -> Element {
dbg!("rendering parent");
let cnt = cx.use_hook(|_| 0);
let cnt = cx.use_hook(|| 0);
*cnt += 1;
if *cnt == 1 {

View file

@ -36,7 +36,7 @@ fn swap_test() {
struct MySharedState(&'static str);
fn app(cx: Scope) -> Element {
let val = cx.use_hook(|_| 0);
let val = cx.use_hook(|| 0);
*val += 1;
cx.provide_context(Rc::new(MySharedState("world!")));

View file

@ -16,7 +16,7 @@ use std::{
pub fn use_atom_ref<T: 'static>(cx: &ScopeState, atom: AtomRef<T>) -> &UseAtomRef<T> {
let root = use_atom_root(cx);
&cx.use_hook(|_| {
&cx.use_hook(|| {
root.initialize(atom);
(
UseAtomRef {

View file

@ -4,7 +4,7 @@ use std::rc::Rc;
// Returns the atom root, initiaizing it at the root of the app if it does not exist.
pub fn use_atom_root(cx: &ScopeState) -> &Rc<AtomRoot> {
cx.use_hook(|_| match cx.consume_context::<Rc<AtomRoot>>() {
cx.use_hook(|| match cx.consume_context::<Rc<AtomRoot>>() {
Some(root) => root,
None => cx.provide_root_context(Rc::new(AtomRoot::new(cx.schedule_update_any()))),
})

View file

@ -4,7 +4,7 @@ use std::rc::Rc;
// Initializes the atom root and retuns it;
pub fn use_init_atom_root(cx: &ScopeState) -> &Rc<AtomRoot> {
cx.use_hook(|_| match cx.consume_context::<Rc<AtomRoot>>() {
cx.use_hook(|| match cx.consume_context::<Rc<AtomRoot>>() {
Some(ctx) => ctx,
None => cx.provide_context(Rc::new(AtomRoot::new(cx.schedule_update_any()))),
})

View file

@ -22,7 +22,7 @@ pub fn use_read_rc<V: 'static>(cx: &ScopeState, f: impl Readable<V>) -> &Rc<V> {
}
}
let inner = cx.use_hook(|_| UseReadInner {
let inner = cx.use_hook(|| UseReadInner {
value: None,
root: root.clone(),
scope_id: cx.scope_id(),

View file

@ -4,7 +4,7 @@ use std::rc::Rc;
pub fn use_set<T: 'static>(cx: &ScopeState, f: impl Writable<T>) -> &Rc<dyn Fn(T)> {
let root = use_atom_root(cx);
cx.use_hook(|_| {
cx.use_hook(|| {
let id = f.unique_id();
let root = root.clone();
root.initialize(f);

View file

@ -33,7 +33,7 @@ use std::{
pub fn use_atom_state<T: 'static>(cx: &ScopeState, f: impl Writable<T>) -> &AtomState<T> {
let root = crate::use_atom_root(cx);
let inner = cx.use_hook(|_| AtomState {
let inner = cx.use_hook(|| AtomState {
value: None,
root: root.clone(),
scope_id: cx.scope_id(),

View file

@ -61,7 +61,7 @@ impl<T> ProvidedStateInner<T> {
///
///
pub fn use_context<T: 'static>(cx: &ScopeState) -> Option<UseSharedState<T>> {
let state = cx.use_hook(|_| {
let state = cx.use_hook(|| {
let scope_id = cx.scope_id();
let root = cx.consume_context::<ProvidedState<T>>();
@ -173,7 +173,7 @@ where
///
///
pub fn use_context_provider<T: 'static>(cx: &ScopeState, f: impl FnOnce() -> T) {
cx.use_hook(|_| {
cx.use_hook(|| {
let state: ProvidedState<T> = Rc::new(RefCell::new(ProvidedStateInner {
value: Rc::new(RefCell::new(f())),
notify_any: cx.schedule_update_any(),

View file

@ -65,7 +65,7 @@ where
G: FnOnce(UnboundedReceiver<M>) -> F,
F: Future<Output = ()> + 'static,
{
cx.use_hook(|_| {
cx.use_hook(|| {
let (tx, rx) = futures_channel::mpsc::unbounded();
let task = cx.push_future(init(rx));
cx.provide_context(CoroutineHandle { tx, task })
@ -76,7 +76,7 @@ where
///
/// See the docs for [`use_coroutine`] for more details.
pub fn use_coroutine_handle<M: 'static>(cx: &ScopeState) -> Option<&CoroutineHandle<M>> {
cx.use_hook(|_| cx.consume_context::<CoroutineHandle<M>>())
cx.use_hook(|| cx.consume_context::<CoroutineHandle<M>>())
.as_ref()
}

View file

@ -34,7 +34,7 @@ where
dependencies: Vec<Box<dyn Any>>,
}
let state = cx.use_hook(move |_| UseEffect {
let state = cx.use_hook(move || UseEffect {
needs_regen: true,
task: Cell::new(None),
dependencies: Vec::new(),

View file

@ -25,7 +25,7 @@ where
F: Future<Output = T> + 'static,
D: UseFutureDep,
{
let state = cx.use_hook(move |_| UseFuture {
let state = cx.use_hook(move || UseFuture {
update: cx.schedule_update(),
needs_regen: Cell::new(true),
slot: Rc::new(Cell::new(None)),

View file

@ -14,7 +14,7 @@ use std::{
};
pub fn use_model<'a, T: 'static>(cx: &'a ScopeState, f: impl FnOnce() -> T) -> UseModel<'a, T> {
let inner = cx.use_hook(|_| UseModelInner {
let inner = cx.use_hook(|| UseModelInner {
update_scheduled: Cell::new(false),
update_callback: cx.schedule_update(),
value: RefCell::new(f()),
@ -78,7 +78,7 @@ pub fn use_model_coroutine<'a, T, F: Future<Output = ()> + 'static>(
_model: UseModel<T>,
_f: impl FnOnce(AppModels) -> F,
) -> UseModelCoroutine {
cx.use_hook(|_| UseModelTaskInner {
cx.use_hook(|| UseModelTaskInner {
task: Default::default(),
});
todo!()

View file

@ -111,7 +111,7 @@ use std::{
/// })
/// ```
pub fn use_ref<T: 'static>(cx: &ScopeState, initialize_refcell: impl FnOnce() -> T) -> &UseRef<T> {
let hook = cx.use_hook(|_| UseRef {
let hook = cx.use_hook(|| UseRef {
update: cx.schedule_update(),
value: Rc::new(RefCell::new(initialize_refcell())),
dirty: Rc::new(Cell::new(false)),

View file

@ -34,7 +34,7 @@ pub fn use_state<T: 'static>(
cx: &ScopeState,
initial_state_fn: impl FnOnce() -> T,
) -> &UseState<T> {
let hook = cx.use_hook(move |_| {
let hook = cx.use_hook(move || {
let current_val = Rc::new(initial_state_fn());
let update_callback = cx.schedule_update();
let slot = Rc::new(RefCell::new(current_val.clone()));

View file

@ -7,7 +7,7 @@ pub fn use_suspense<R: 'static, F: Future<Output = R> + 'static>(
create_future: impl FnOnce() -> F,
render: impl FnOnce(&R) -> Element,
) -> Element {
let sus = cx.use_hook(|_| {
let sus = cx.use_hook(|| {
let fut = create_future();
let wip_value: Rc<Cell<Option<R>>> = Default::default();

View file

@ -77,7 +77,7 @@ pub struct LinkProps<'a> {
/// }
/// ```
pub fn Link<'a>(cx: Scope<'a, LinkProps<'a>>) -> Element {
let svc = cx.use_hook(|_| cx.consume_context::<Arc<RouterCore>>());
let svc = cx.use_hook(|| cx.consume_context::<Arc<RouterCore>>());
let LinkProps {
to,

View file

@ -34,7 +34,7 @@ pub struct RedirectProps<'a> {
pub fn Redirect<'a>(cx: Scope<'a, RedirectProps<'a>>) -> Element {
let router = use_router(&cx);
let immediate_redirect = cx.use_hook(|_| {
let immediate_redirect = cx.use_hook(|| {
if let Some(from) = cx.props.from {
router.register_total_route(from.to_string(), cx.scope_id());
false

View file

@ -28,10 +28,10 @@ pub struct RouteProps<'a> {
/// ```
pub fn Route<'a>(cx: Scope<'a, RouteProps<'a>>) -> Element {
let router_root = cx
.use_hook(|_| cx.consume_context::<Arc<RouterCore>>())
.use_hook(|| cx.consume_context::<Arc<RouterCore>>())
.as_ref()?;
cx.use_hook(|_| {
cx.use_hook(|| {
// create a bigger, better, longer route if one above us exists
let total_route = match cx.consume_context::<RouteContext>() {
Some(ctx) => ctx.total_route,

View file

@ -37,7 +37,7 @@ pub struct RouterProps<'a> {
/// Will fallback to HashRouter is BrowserRouter is not available, or through configuration.
#[allow(non_snake_case)]
pub fn Router<'a>(cx: Scope<'a, RouterProps<'a>>) -> Element {
let svc = cx.use_hook(|_| {
let svc = cx.use_hook(|| {
cx.provide_context(RouterCore::new(
&cx,
RouterCfg {

View file

@ -7,7 +7,7 @@ use url::Url;
/// context of a [`Router`]. If this function is called outside of a `Router`
/// component it will panic.
pub fn use_route(cx: &ScopeState) -> &UseRoute {
let handle = cx.use_hook(|_| {
let handle = cx.use_hook(|| {
let router = cx
.consume_context::<RouterService>()
.expect("Cannot call use_route outside the scope of a Router component");

View file

@ -3,7 +3,7 @@ use dioxus::core::ScopeState;
/// This hook provides access to the `RouterService` for the app.
pub fn use_router(cx: &ScopeState) -> &RouterService {
cx.use_hook(|_| {
cx.use_hook(|| {
cx.consume_context::<RouterService>()
.expect("Cannot call use_route outside the scope of a Router component")
})

View file

@ -16,7 +16,7 @@ use dioxus_core::*;
/// The closure will panic if the provided script is not valid JavaScript code
/// or if it returns an uncaught error.
pub fn use_eval<S: std::string::ToString>(cx: &ScopeState) -> &dyn Fn(S) {
cx.use_hook(|_| {
cx.use_hook(|| {
|script: S| {
js_sys::Function::new_no_args(&script.to_string())
.call0(&wasm_bindgen::JsValue::NULL)

View file

@ -0,0 +1,182 @@
<div align="center">
<h1>Dioxus</h1>
</div>
<div align="center">
<!-- Crates version -->
<a href="https://crates.io/crates/dioxus">
<img src="https://img.shields.io/crates/v/dioxus.svg?style=flat-square"
alt="Crates.io version" />
</a>
<!-- Downloads -->
<a href="https://crates.io/crates/dioxus">
<img src="https://img.shields.io/crates/d/dioxus.svg?style=flat-square"
alt="Download" />
</a>
<!-- docs -->
<a href="https://docs.rs/dioxus">
<img src="https://img.shields.io/badge/docs-latest-blue.svg?style=flat-square"
alt="docs.rs docs" />
</a>
<!-- CI -->
<a href="https://github.com/jkelleyrtp/dioxus/actions">
<img src="https://github.com/dioxuslabs/dioxus/actions/workflows/main.yml/badge.svg"
alt="CI status" />
</a>
<!--Awesome -->
<a href="https://github.com/dioxuslabs/awesome-dioxus">
<img src="https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg" alt="Awesome Page" />
</a>
<!-- Discord -->
<a href="https://discord.gg/XgGxMSkvUM">
<img src="https://img.shields.io/discord/899851952891002890.svg?logo=discord&style=flat-square" alt="Discord Link" />
</a>
</div>
<div align="center">
<h3>
<a href="https://dioxuslabs.com"> Website </a>
<span> | </span>
<a href="https://github.com/DioxusLabs/example-projects"> Exemplos </a>
<span> | </span>
<a href="https://dioxuslabs.com/guide"> Guia </a>
<span> | </span>
<a href="https://github.com/DioxusLabs/dioxus/blob/master/notes/README/ZH_CN.md"> 中文 </a>
</h3>
</div>
<br/>
> 💡 Este é um documento traduzido voluntariamente. Se você viu erros de tradução e/ou erros de digitação, entre em contato: [@amindWalker](https://github.com/amindWalker), [GMail](bhrochamail@gmail.com) ou **_melhor ainda, envie um PR_**.
<br>
**Dioxus** é um framework ergonômico para construir interfaces de forma portátil, rápida, escalável e robusta com a linguagem de programação Rust.
```rust
fn app(cx: Scope) -> Element {
let mut count = use_state(&cx, || 0);
cx.render(rsx! {
h1 { "High-Five counter: {count}" }
button { onclick: move |_| count += 1, "Up high!" }
button { onclick: move |_| count -= 1, "Down low!" }
})
}
```
O Dioxus pode ser usado para desenvolver aplicativos Web, Desktop, sites estáticos, TUI, LiveView e mais. O Dioxus é inteiramente agnóstico de renderizador e pode ser usado como uma plataforma para qualquer renderizador.
Se você conhece React, então você já conhece o Dioxus.
### Funções Únicas:
- Aplicativos Desktop rodam nativamente (sem ElectronJS!) em menos de 10 linhas de código.
- Incrivelmente ergonômico e um poderoso gerenciador de estados.
- Documentação compreensiva - guias e explicações ao apontar o mouse para todos os elementos HTML e eventos.
- Extremamente eficiente em memória - 0 alocações globais para componentes com estado-estável.
- Agendamento assíncrono de canais-múltiplos para suporte de `async` de primera-classe.
- E mais! Leia as [publicações de lançamento](https://dioxuslabs.com/blog/introducing-dioxus/).
## Começando com...
<table style="width:100%" align="center">
<tr>
<th><a href="https://dioxuslabs.com/guide/">Tutorial</a></th>
<th><a href="https://dioxuslabs.com/reference/web">Web</a></th>
<th><a href="https://dioxuslabs.com/reference/desktop/">Desktop</a></th>
<th><a href="https://dioxuslabs.com/reference/ssr/">SSR</a></th>
<th><a href="https://dioxuslabs.com/reference/mobile/">Móvel</a></th>
<tr>
</table>
## Projetos de Exemplo:
| Navegador de Arquivos (Desktop) | WiFi Scanner (Desktop) | TodoMVC (Todas as Plataformas) | E-commerce com Tailwind (SSR/LiveView) |
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [![Explorador de Arquivos](https://github.com/DioxusLabs/example-projects/raw/master/file-explorer/image.png)](https://github.com/DioxusLabs/example-projects/blob/master/file-explorer) | [![Wifi Scanner Demo](https://github.com/DioxusLabs/example-projects/raw/master/wifi-scanner/demo_small.png)](https://github.com/DioxusLabs/example-projects/blob/master/wifi-scanner) | [![TodoMVC example](https://github.com/DioxusLabs/example-projects/raw/master/todomvc/example.png)](https://github.com/DioxusLabs/example-projects/blob/master/todomvc) | [![Exemplo de E-commerce](https://github.com/DioxusLabs/example-projects/raw/master/ecommerce-site/demo.png)](https://github.com/DioxusLabs/example-projects/blob/master/ecommerce-site) |
Veja a página [awesome-dioxus](https://github.com/DioxusLabs/awesome-dioxus) para uma lista curada do conteúdo dentro do ecossistema do Dioxus.
## Porquê o Dioxus e porquê o Rust?
TypeScript é uma adição fantástica ao JavaScript, mas ainda é fundamentalmente JavaScript. TS executa ligeiramente mais devagar, tem várias opções de configurações diferentes e nem todos os pacotes estão propriamente tipados.
Apenas por usar o Rust, nós ganhamos:
- Tipos estáticos para _todas_ as bibliotecas por padrão
- Imutabilidade por padrão
- Um sistema de módulos simples e intuitivo
- Documentação integrada na própria linguagem (`go to source` _de fato vai até a fonte_)
- Padrões de combinação avançados
- Iteradores limpos, eficientes e combináveis
- Testes de Unidade/Integração em linha integrados à linguagem
- O melhor da classe em Tratamento de Erros
- Biblioteca padrão poderosa e sensata
- Sistema de macros flexível
- Acesso ao `crates.io`
Especificamente, o Dioxus providencia para nós muitas outras garantias:
- Estrutura de Dados imutável apropriada
- Garantias para Tratamento de Erros (assim você pode dormir em paz à noite sem se preocupar com erros do tipo `undefined`)
- Desempenho móvel nativo
- Acesso direto ao sistema de Entrada/Saída (IO)
E muito mais. Dioxus faz com que aplicativos em Rust sejam rápidos de escrever como os de React, mas permite mais robustez dando ao sua equipe frontend mais confiança em desenvolver grandes mudanças em pouco tempo.
## Porquê não o Dioxus?
Você não deve usar o Dioxus se:
- Você não gosta da metodologia do React Hooks para o frontend
- Você precisa de um renderizador personalizado
- Você quer suporte para navegadores que não tenham suporte ao WASM ou asm.js
- Você precisa de uma solução `Send` + `Sync` (o Dioxus ainda não é `thread-safe`)
## Comparação com outras frameworks de UI em Rust:
Dioxus primeiramente enfatiza a **experiência do desenvolvedor** e a **familiaridade com os princípios do React**.
- [Yew](https://github.com/yewstack/yew): prefere o padrão `elm`, não há `props` emprestadas, suporta SSR (sem `hydration`), sem suporte direto para Desktop/Móvel.
- [Percy](https://github.com/chinedufn/percy): suporta SSR, mas com menos ênfase em gerenciamento de estado e tratamento de eventos.
- [Sycamore](https://github.com/sycamore-rs/sycamore): sem `VirtualDOM` usando controle preciso de reatividade, mas sem suporte direto à aplicativos Desktop/Móvel.
- [Dominator](https://github.com/Pauan/rust-dominator): alternativa zero-custo baseada em sinais, menos ênfase em comunidade e documentação.
- [Azul](https://azul.rs): renderizador HTML/CSS totalmente nativo para aplicações Desktop, sem suporte para Web/SSR.
## Paridade com React e Progresso
Dioxus é fortemente inspirado pelo React, mas nós queremos que sua transição pareça como um aprimoramento. Dioxus está _quase_ lá, mas ainda faltam alguma funções chave. Isto inclui:
- Portais
- Suspensão integrada ao SSR
- Componentes de Servidor / Segmentador de Pacotes / Execução Tardia (Lazy)
Dioxus é único no ecossistema do Rust por suportar:
- Componentes com propriedade que são emprestadas dos seus parentes
- SSR com `hydration` feito pelo Cliente
- Suporte à aplicação Desktop
Para mais informações sobre quais funções estão atualmente disponíveis e para o progresso futuro, veja [O Guia](https://dioxuslabs.com/guide/).
## Projeto dentro do ecossistema Dioxus
Quer adentrar e ajudar a construir o futuro do frontend em Rust? Há um vasto número de lugares em que você pode contribuir e fazer uma grande diferença:
- [TUI renderer](https://github.com/dioxusLabs/rink)
- [Ferramentas CLI](https://github.com/dioxusLabs/cli)
- [Documentação e Exemplos de Projeto](https://github.com/dioxusLabs/docsite)
- LiveView e Servidor Web
- Sistema de Componentes prontos
## Licença
Este projeto é licenciado sob a licença MIT.
[licença mit]: https://github.com/DioxusLabs/dioxus/blob/master/LICENSE-MIT
### Contribuições
A menos que você explicitamente ateste o contrário, qualquer contribuição feita ao Dioxus por você será licenciada de acordo com a licença MIT sem nenhum outro termo ou condição.