|
@@ -11,46 +11,50 @@ Implementing the renderer is fairly straightforward. The renderer needs to:
|
|
|
1. Handle the stream of edits generated by updates to the virtual DOM
|
|
|
2. Register listeners and pass events into the virtual DOM's event system
|
|
|
|
|
|
-Essentially, your renderer needs to implement the `RealDom` trait and generate `EventTrigger` objects to update the VirtualDOM. From there, you'll have everything needed to render the VirtualDOM to the screen.
|
|
|
+Essentially, your renderer needs to process edits and generate events to update the VirtualDOM. From there, you'll have everything needed to render the VirtualDOM to the screen.
|
|
|
|
|
|
Internally, Dioxus handles the tree relationship, diffing, memory management, and the event system, leaving as little as possible required for renderers to implement themselves.
|
|
|
|
|
|
-For reference, check out the javascript interperter or tui renderer as a starting point for your custom renderer.
|
|
|
+For reference, check out the [javascript interpreter](https://github.com/DioxusLabs/dioxus/tree/master/packages/interpreter) or [tui renderer](https://github.com/DioxusLabs/dioxus/tree/master/packages/tui) as a starting point for your custom renderer.
|
|
|
|
|
|
-## DomEdits
|
|
|
+## Templates
|
|
|
|
|
|
-The "DomEdit" type is a serialized enum that represents an atomic operation occurring on the RealDom. The variants roughly follow this set:
|
|
|
+Dioxus is built around the concept of [Templates](https://docs.rs/dioxus-core/latest/dioxus_core/prelude/struct.Template.html). Templates describe a UI tree known at compile time with dynamic parts filled at runtime. This is useful internally to make skip diffing static nodes, but it is also useful for the renderer to reuse parts of the UI tree. This can be useful for things like a list of items. Each item could contain some static parts and some dynamic parts. The renderer can use the template to create a static part of the UI once, clone it for each element in the list, and then fill in the dynamic parts.
|
|
|
+
|
|
|
+## Mutations
|
|
|
+
|
|
|
+The `Mutation` type is a serialized enum that represents an operation that should be applied to update the UI. The variants roughly follow this set:
|
|
|
|
|
|
```rust
|
|
|
-enum DomEdit {
|
|
|
- PushRoot,
|
|
|
+enum Mutation {
|
|
|
AppendChildren,
|
|
|
+ AssignId,
|
|
|
+ CreatePlaceholder,
|
|
|
+ CreateTextNode,
|
|
|
+ HydrateText,
|
|
|
+ LoadTemplate,
|
|
|
ReplaceWith,
|
|
|
+ ReplacePlaceholder,
|
|
|
InsertAfter,
|
|
|
InsertBefore,
|
|
|
- Remove,
|
|
|
- CreateTextNode,
|
|
|
- CreateElement,
|
|
|
- CreateElementNs,
|
|
|
- CreatePlaceholder,
|
|
|
+ SetAttribute,
|
|
|
+ SetText,
|
|
|
NewEventListener,
|
|
|
RemoveEventListener,
|
|
|
- SetText,
|
|
|
- SetAttribute,
|
|
|
- RemoveAttribute,
|
|
|
- PopRoot,
|
|
|
+ Remove,
|
|
|
+ PushRoot,
|
|
|
}
|
|
|
```
|
|
|
|
|
|
The Dioxus diffing mechanism operates as a [stack machine](https://en.wikipedia.org/wiki/Stack_machine) where the "push_root" method pushes a new "real" DOM node onto the stack and "append_child" and "replace_with" both remove nodes from the stack.
|
|
|
|
|
|
|
|
|
-### An example
|
|
|
+### An Example
|
|
|
|
|
|
-For the sake of understanding, lets consider this example – a very simple UI declaration:
|
|
|
+For the sake of understanding, let's consider this example – a very simple UI declaration:
|
|
|
|
|
|
```rust
|
|
|
-rsx!( h1 {"hello world"} )
|
|
|
+rsx!( h1 {"count {x}"} )
|
|
|
```
|
|
|
|
|
|
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.
|
|
@@ -66,7 +70,7 @@ stack: [
|
|
|
]
|
|
|
```
|
|
|
|
|
|
-Next, Dioxus will encounter the h1 node. The diff algorithm decides that this node needs to be created, so Dioxus will generate the DomEdit `CreateElement`. When the renderer receives this instruction, it will create an unmounted node and push into its own stack:
|
|
|
+Next, Dioxus will encounter the h1 node. The diff algorithm decides that this node needs to be created, so Dioxus will generate the DomEdit `CreateElement`. When the renderer receives this instruction, it will create an unmounted node and push it into its own stack:
|
|
|
|
|
|
```rust
|
|
|
instructions: [
|
|
@@ -91,7 +95,7 @@ stack: [
|
|
|
"hello world"
|
|
|
]
|
|
|
```
|
|
|
-Remember, the text node is not attached to anything (it is unmounted) so Dioxus needs to generate an Edit that connects the text node to the h1 element. It depends on the situation, but in this case we use `AppendChildren`. This pops the text node off the stack, leaving the h1 element as the next element in line.
|
|
|
+Remember, the text node is not attached to anything (it is unmounted) so Dioxus needs to generate an Edit that connects the text node to the h1 element. It depends on the situation, but in this case, we use `AppendChildren`. This pops the text node off the stack, leaving the h1 element as the next element in line.
|
|
|
|
|
|
```rust
|
|
|
instructions: [
|
|
@@ -141,13 +145,13 @@ Over time, our stack looked like this:
|
|
|
[]
|
|
|
```
|
|
|
|
|
|
-Notice how our stack is empty once UI has been mounted. Conveniently, this approach completely separates the VirtualDOM and the Real DOM. Additionally, these edits are serializable, meaning we can even manage UIs across a network connection. This little stack machine and serialized edits makes Dioxus independent of platform specifics.
|
|
|
+Notice how our stack is empty once UI has been mounted. Conveniently, this approach completely separates the Virtual DOM and the Real DOM. Additionally, these edits are serializable, meaning we can even manage UIs across a network connection. This little stack machine and serialized edits make Dioxus independent of platform specifics.
|
|
|
|
|
|
Dioxus is also really fast. Because Dioxus splits the diff and patch phase, it's able to make all the edits to the RealDOM in a very short amount of time (less than a single frame) making rendering very snappy. It also allows Dioxus to cancel large diffing operations if higher priority work comes in while it's diffing.
|
|
|
|
|
|
It's important to note that there _is_ one layer of connectedness between Dioxus and the renderer. Dioxus saves and loads elements (the PushRoot edit) with an ID. Inside the VirtualDOM, this is just tracked as a u64.
|
|
|
|
|
|
-Whenever a `CreateElement` edit is generated during diffing, Dioxus increments its node counter and assigns that new element its current NodeCount. The RealDom is responsible for remembering this ID and pushing the correct node when PushRoot(ID) is generated. Dioxus reclaims IDs of elements when removed. To stay in sync with Dioxus you can use a sparce Vec (Vec<Option<T>>) with possibly unoccupied items. You can use the ids as indexes into the Vec for elements, and grow the Vec when a id does not exist.
|
|
|
+Whenever a `CreateElement` edit is generated during diffing, Dioxus increments its node counter and assigns that new element its current NodeCount. The RealDom is responsible for remembering this ID and pushing the correct node when PushRoot(ID) is generated. Dioxus reclaims the IDs of elements when removed. To stay in sync with Dioxus you can use a sparse Vec (Vec<Option<T>>) with possibly unoccupied items. You can use the ids as indexes into the Vec for elements, and grow the Vec when an id does not exist.
|
|
|
|
|
|
This little demo serves to show exactly how a Renderer would need to process an edit stream to build UIs. A set of serialized DomEditss for various demos is available for you to test your custom renderer against.
|
|
|
|
|
@@ -243,16 +247,16 @@ You've probably noticed that many elements in the `rsx!` macros support on-hover
|
|
|
|
|
|
# Native Core
|
|
|
|
|
|
-If you are creating a renderer in rust, native core provides some utilites to implement a renderer. It provides an abstraction over DomEdits and handles layout for you.
|
|
|
+If you are creating a renderer in rust, native-core provides some utilities to implement a renderer. It provides an abstraction over DomEdits and handles the layout for you.
|
|
|
|
|
|
## RealDom
|
|
|
|
|
|
-The `RealDom` is a higher level abstraction over updating the Dom. It updates with `DomEdits` and provides a way to incrementally update the state of nodes based on what attributes change.
|
|
|
+The `RealDom` is a higher-level abstraction over updating the Dom. It updates with `DomEdits` and provides a way to incrementally update the state of nodes based on what attributes change.
|
|
|
|
|
|
### Example
|
|
|
|
|
|
Let's build a toy renderer with borders, size, and text color.
|
|
|
-Before we start lets take a look at an example element we can render:
|
|
|
+Before we start let's take a look at an example element we can render:
|
|
|
```rust
|
|
|
cx.render(rsx!{
|
|
|
div{
|
|
@@ -265,7 +269,7 @@ cx.render(rsx!{
|
|
|
})
|
|
|
```
|
|
|
|
|
|
-In this tree the color depends on the parent's color. The size depends on the childrens size, the current text, and a text size. The border depends on only the current node.
|
|
|
+In this tree, the color depends on the parent's color. The size depends on the children's size, the current text, and the text size. The border depends on only the current node.
|
|
|
|
|
|
In the following diagram arrows represent dataflow:
|
|
|
|
|
@@ -312,7 +316,7 @@ In the following diagram arrows represent dataflow:
|
|
|
[//]: # " end"
|
|
|
[//]: # " end"
|
|
|
|
|
|
-To help in building a Dom, native core provides four traits: State, ChildDepState, ParentDepState, and NodeDepState and a RealDom struct. The ChildDepState, ParentDepState, and NodeDepState provide a way to describe how some information in a node relates to that of its relatives. By providing how to build a single node from its relations, native-core will derive a way to update the state of all nodes for you with ```#[derive(State)]```. Once you have a state you can provide it as a generic to RealDom. RealDom provides all of the methods to interact and update your new dom.
|
|
|
+To help in building a Dom, native-core provides four traits: State, ChildDepState, ParentDepState, NodeDepState, and a RealDom struct. The ChildDepState, ParentDepState, and NodeDepState provide a way to describe how some information in a node relates to that of its relatives. By providing how to build a single node from its relations, native-core will derive a way to update the state of all nodes for you with ```#[derive(State)]```. Once you have a state you can provide it as a generic to RealDom. RealDom provides all of the methods to interact and update your new dom.
|
|
|
|
|
|
```rust
|
|
|
use dioxus_native_core::node_ref::*;
|
|
@@ -447,7 +451,7 @@ struct ToyState {
|
|
|
}
|
|
|
```
|
|
|
|
|
|
-Now that we have our state, we can put it to use in our dom. Re can update the dom with update_state to update the structure of the dom (adding, removing, and chaning properties of nodes) and then apply_mutations to update the ToyState for each of the nodes that changed.
|
|
|
+Now that we have our state, we can put it to use in our dom. Re can update the dom with update_state to update the structure of the dom (adding, removing, and changing properties of nodes) and then apply_mutations to update the ToyState for each of the nodes that changed.
|
|
|
```rust
|
|
|
fn main(){
|
|
|
fn app(cx: Scope) -> Element {
|
|
@@ -490,7 +494,7 @@ fn main(){
|
|
|
```
|
|
|
|
|
|
## Layout
|
|
|
-For most platforms the layout of the Elements will stay the same. The layout_attributes module provides a way to apply html attributes to a stretch layout style.
|
|
|
+For most platforms, the layout of the Elements will stay the same. The layout_attributes module provides a way to apply HTML attributes to a stretch layout style.
|
|
|
|
|
|
## Conclusion
|
|
|
-That should be it! You should have nearly all the knowledge required on how to implement your own renderer. We're super interested in seeing Dioxus apps brought to custom desktop renderers, mobile renderer, video game UI, and even augmented reality! If you're interesting in contributing to any of the these projects, don't be afraid to reach out or join the community.
|
|
|
+That should be it! You should have nearly all the knowledge required on how to implement your own renderer. We're super interested in seeing Dioxus apps brought to custom desktop renderers, mobile renderers, video game UI, and even augmented reality! If you're interested in contributing to any of these projects, don't be afraid to reach out or join the [community](https://discord.gg/XgGxMSkvUM).
|