Add PT-BR translations (#456)
* Translate final.md, hello_world.md, README.md, ROADMAP.md and SUMMARY.md * Translatation complete * Internationalization: move guide to en folder and preserve its history. (#494) * Clean files and build mdBook with localization feature * Rebase, remove leftovers and check book builds for every language: ✅ Co-authored-by: Jon Kelley <jkelleyrtp@gmail.com>
This commit is contained in:
1 parent
2369b0cc7f
commit
6ea076fa7f
69 files changed
+4239
-13
No files matched your search
@@ -0,0 +1,25 @@
|
||||
# Dioxus: Guias Avançados e Referência
|
||||
|
||||

|
||||
|
||||
**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.
|
||||
@@ -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)
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -0,0 +1 @@
|
||||
# Guias Avançados
|
||||
@@ -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}
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
@@ -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}
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
@@ -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!
|
||||
@@ -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)
|
||||
Reference in new issue
Block a user