Skip to main content

dioxus_core_template/
lib.rs

1//! Static template storage for Dioxus views.
2//!
3//! The `rsx!` macro lowers each view into two pieces:
4//!
5//! - a static [`Template`] describing the stable element, text, and static
6//!   attribute structure
7//! - runtime dynamic node and dynamic attribute arrays for one render
8//!
9//! The template uses compact paths and anchors to connect those runtime values
10//! back to stable locations in the static tree.
11//!
12//! ## Annotated RSX lowering
13//!
14//! Consider this `rsx!` block:
15//!
16//! ```rust,ignore
17//! rsx! {
18//!     {before}
19//!     div { class: "{class_name}",
20//!         "Hello "
21//!         strong { "{name}" }
22//!         {badge}
23//!         "{count}"
24//!         span { "!" }
25//!     }
26//!     {after}
27//! }
28//! ```
29//!
30//! Dynamic nodes do not receive their own paths. Paths are only stored when an
31//! emitted anchor needs to point to a static node. The path
32//! notation below is a tree walk: `0` means "next child", and `1` means "next
33//! sibling". The empty path is the vnode render-parent site; it is not a real
34//! static node.
35//!
36//! This table includes only paths that are actually emitted in anchor slot
37//! targets for the example above:
38//!
39//! ```text
40//! Path walk    Last static node?    Why it is emitted
41//! 0            false                root anchor for div / node before div
42//! 0            false                dynamic class attrs on div
43//! 001          true                 dynamic text inside strong
44//! 0011         false                badge and count before span
45//! 0            true                 trailing root dynamic node after div
46//! ```
47//!
48//! There is no emitted path for `"Hello "` because no dynamic slot is anchored
49//! to that static text node. The same path can appear with different parent
50//! ownership: a root-level anchor at `0` can insert before `div`, while an
51//! element-owned anchor at `0` applies dynamic attributes to `div`.
52//!
53//! The low tag bit on an anchor path records whether the path is the last
54//! static node before the dynamic slot. When it is false, dynamic nodes insert
55//! before the static node at `path`. When it is true, dynamic nodes insert
56//! after that static node, or append to it when the path is also the anchor's
57//! owning element.
58//!
59//! Anchors are created for dynamic node or attribute groups and for every root static node. A
60//! non-root static element appears as an anchor parent only when it owns dynamic attributes or a
61//! direct dynamic child insertion position. Root-level anchors use `None` as their parent.
62//!
63//! Dynamic nodes and dynamic attributes are pushed into separate runtime arrays
64//! as the typed view renders. An element's dynamic attributes are pushed before
65//! its dynamic children, so the example above produces values like this:
66//!
67//! ```text
68//! Node index    Runtime node
69//! 0             before
70//! 1             text from "{name}"
71//! 2             badge
72//! 3             text from "{count}"
73//! 4             after
74//!
75//! Attr index    Runtime attrs
76//! 0             class from "{class_name}"
77//! ```
78//!
79//! Anchors map ranges from those arrays to static paths plus the last-static
80//! flag. A root static node always has an anchor, even when it owns no dynamic
81//! values, so renderers can use the same anchor list for hydration and template
82//! cloning.
83//!
84//! ```text
85//! Nodes    Attrs    Parent element op    Path    Last?    Meaning
86//! 0..1     0..0     None                 0       false    root node before div
87//! 1..1     0..1     div                  0       false    dynamic class attrs for div
88//! 1..2     1..1     strong               001     true     "{name}" inside strong
89//! 2..4     1..1     div                  0011    false    badge and count before span
90//! 4..5     1..1     None                 0       true     trailing root node
91//! ```
92//!
93//! Adjacent dynamic nodes at the same insertion position share one anchor. In
94//! the example, `{badge}` and `"{count}"` are both before `span`, so they are
95//! represented by the single `2..4` node range.
96//!
97//! The stored anchor slice is in source/template order.
98//!
99mod anchor;
100mod data;
101mod op;
102mod path;
103mod raw;
104#[cfg(feature = "serialize")]
105mod serialization;
106mod storage;
107
108pub use anchor::TemplateAnchor;
109pub use data::{
110    StaticTemplateAttribute, StaticTemplateAttributeIter, StaticTemplateElement,
111    StaticTemplateNode, StaticTemplateNodeIter, StaticTemplateText, Template,
112};
113pub use op::DecodedTemplateOp;
114pub use path::{
115    TEMPLATE_SLOT_PATH_MAX_PATH_BITS, TemplatePath, TemplateSlotPath, TemplateSlotTarget,
116};
117pub use raw::TemplateRawTree;
118#[cfg(feature = "serialize")]
119pub use serialization::{deserialize_option_leaky, deserialize_string_leaky};
120#[cfg(debug_assertions)]
121pub use storage::build_runtime_template;
122pub use storage::{
123    RuntimeTemplateBuilder, TEMPLATE_STORAGE_DYNAMIC_CAP, TEMPLATE_STORAGE_MAX_CAP,
124    TEMPLATE_STORAGE_OPS_CAP, TEMPLATE_STORAGE_STRING_CAP, TemplateLoweringCursor, TemplateStorage,
125};