Subtree memorization / reactive templates (#488)
This commit adds subtree memoization to Dioxus. Subtree memoization is basically a compile-time step that drastically reduces the amount of work the diffing engine needs to do at runtime by extracting non-changing nodes out into a static "template." Templates are then understood by the various renderers in the ecosystem as a faster way of rendering the same items. For example, in the web, templates are simply a set of DOM Nodes created once and then cloned later. This is the same pattern frameworks like Lithtml and SolidJS use to achieve near-perfect performance. Subtree memoization adds an additional level of complexity to Dioxus. The RSX macro needs to be much smarter to identify changing/nonchanging nodes and generate a mapping between the Template and its runtime counterparts. This commit represents a working starter point for this work, adding support for templates for the web, desktop, liveview, ssr, and native-core renderers. In the future we will try to shrink code generation, generally improve performance, and simplify our implementation.
This commit is contained in:
1 parent
b32fd2d2cd
commit
047ed1e553
80 files changed
+6774
-2552
No files matched your search
@@ -17,12 +17,6 @@ dioxus-html = { path = "../html", version = "^0.2.1", optional = true }
|
||||
dioxus-core-macro = { path = "../core-macro", version = "^0.2.1", optional = true }
|
||||
dioxus-hooks = { path = "../hooks", version = "^0.2.1", optional = true }
|
||||
dioxus-rsx = { path = "../rsx", optional = true }
|
||||
dioxus-rsx-interpreter = { path = "../rsx_interpreter", optional = true }
|
||||
|
||||
|
||||
# dioxus-interpreter-js = { path = "./packages/interpreter", version = "^0.2.1", optional = true }
|
||||
# dioxus-native-core = { path = "./packages/native-core", version = "^0.2.0", optional = true }
|
||||
# dioxus-native-core-macro = { path = "./packages/native-core-macro", version = "^0.2.0", optional = true }
|
||||
|
||||
[features]
|
||||
default = ["macro", "hooks", "html"]
|
||||
@@ -30,8 +24,7 @@ macro = ["dioxus-core-macro", "dioxus-rsx"]
|
||||
html = ["dioxus-html"]
|
||||
hooks = ["dioxus-hooks"]
|
||||
hot-reload = [
|
||||
"dioxus-core-macro/hot-reload",
|
||||
"dioxus-rsx-interpreter",
|
||||
"dioxus-core/hot-reload",
|
||||
]
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@@ -33,10 +33,4 @@ pub mod prelude {
|
||||
|
||||
#[cfg(feature = "html")]
|
||||
pub use dioxus_elements::{GlobalAttributes, SvgAttributes};
|
||||
|
||||
#[cfg(feature = "hot-reload")]
|
||||
pub use dioxus_rsx_interpreter::{
|
||||
captuered_context::{CapturedContext, FormattedArg, IfmtArgs},
|
||||
get_line_num, resolve_scope, CodeLocation, RsxContext,
|
||||
};
|
||||
}
|
||||
@@ -30,12 +30,29 @@ fn test_original_diff() {
|
||||
assert_eq!(
|
||||
mutations.edits,
|
||||
[
|
||||
CreateElement { root: 1, tag: "div" },
|
||||
CreateElement { root: 2, tag: "div" },
|
||||
CreateTextNode { root: 3, text: "Hello, world!" },
|
||||
AppendChildren { many: 1 },
|
||||
CreateTemplate { id: 0 },
|
||||
CreateElementTemplate {
|
||||
root: 4503599627370495,
|
||||
tag: "div",
|
||||
locally_static: true,
|
||||
fully_static: true
|
||||
},
|
||||
CreateElementTemplate {
|
||||
root: 4503599627370496,
|
||||
tag: "div",
|
||||
locally_static: true,
|
||||
fully_static: true
|
||||
},
|
||||
CreateTextNodeTemplate {
|
||||
root: 4503599627370497,
|
||||
text: "Hello, world!",
|
||||
locally_static: true
|
||||
},
|
||||
AppendChildren { many: 1 },
|
||||
AppendChildren { many: 1 },
|
||||
FinishTemplate { len: 1 },
|
||||
CreateTemplateRef { id: 1, template_id: 0 },
|
||||
AppendChildren { many: 1 }
|
||||
]
|
||||
);
|
||||
}
|
||||
@@ -66,18 +83,49 @@ fn create() {
|
||||
assert_eq!(
|
||||
mutations.edits,
|
||||
[
|
||||
CreateElement { root: 1, tag: "div" },
|
||||
CreateElement { root: 2, tag: "div" },
|
||||
CreateTextNode { root: 3, text: "Hello, world!" },
|
||||
CreateElement { root: 4, tag: "div" },
|
||||
CreateElement { root: 5, tag: "div" },
|
||||
CreateTextNode { root: 6, text: "hello" },
|
||||
CreateTextNode { root: 7, text: "world" },
|
||||
AppendChildren { many: 2 },
|
||||
CreateTemplate { id: 0 },
|
||||
CreateElementTemplate {
|
||||
root: 4503599627370495,
|
||||
tag: "div",
|
||||
locally_static: true,
|
||||
fully_static: false
|
||||
},
|
||||
CreateElementTemplate {
|
||||
root: 4503599627370496,
|
||||
tag: "div",
|
||||
locally_static: true,
|
||||
fully_static: false
|
||||
},
|
||||
CreateTextNodeTemplate {
|
||||
root: 4503599627370497,
|
||||
text: "Hello, world!",
|
||||
locally_static: true
|
||||
},
|
||||
CreateElementTemplate {
|
||||
root: 4503599627370498,
|
||||
tag: "div",
|
||||
locally_static: true,
|
||||
fully_static: false
|
||||
},
|
||||
CreateElementTemplate {
|
||||
root: 4503599627370499,
|
||||
tag: "div",
|
||||
locally_static: true,
|
||||
fully_static: false
|
||||
},
|
||||
CreatePlaceholderTemplate { root: 4503599627370500 },
|
||||
AppendChildren { many: 1 },
|
||||
AppendChildren { many: 1 },
|
||||
AppendChildren { many: 2 },
|
||||
AppendChildren { many: 1 },
|
||||
AppendChildren { many: 1 },
|
||||
FinishTemplate { len: 1 },
|
||||
CreateTemplateRef { id: 1, template_id: 0 },
|
||||
EnterTemplateRef { root: 1 },
|
||||
CreateTextNode { root: 2, text: "hello" },
|
||||
CreateTextNode { root: 3, text: "world" },
|
||||
ReplaceWith { root: 4503599627370500, m: 2 },
|
||||
ExitTemplateRef {},
|
||||
AppendChildren { many: 1 }
|
||||
]
|
||||
);
|
||||
}
|
||||
@@ -99,16 +147,20 @@ fn create_list() {
|
||||
assert_eq!(
|
||||
mutations.edits,
|
||||
[
|
||||
CreateElement { root: 1, tag: "div" },
|
||||
CreateTextNode { root: 2, text: "hello" },
|
||||
CreateTemplate { id: 0 },
|
||||
CreateElementTemplate {
|
||||
root: 4503599627370495,
|
||||
tag: "div",
|
||||
locally_static: true,
|
||||
fully_static: true
|
||||
},
|
||||
CreateTextNodeTemplate { root: 4503599627370496, text: "hello", locally_static: true },
|
||||
AppendChildren { many: 1 },
|
||||
CreateElement { root: 3, tag: "div" },
|
||||
CreateTextNode { root: 4, text: "hello" },
|
||||
AppendChildren { many: 1 },
|
||||
CreateElement { root: 5, tag: "div" },
|
||||
CreateTextNode { root: 6, text: "hello" },
|
||||
AppendChildren { many: 1 },
|
||||
AppendChildren { many: 3 },
|
||||
FinishTemplate { len: 1 },
|
||||
CreateTemplateRef { id: 1, template_id: 0 },
|
||||
CreateTemplateRef { id: 2, template_id: 0 },
|
||||
CreateTemplateRef { id: 3, template_id: 0 },
|
||||
AppendChildren { many: 3 }
|
||||
]
|
||||
);
|
||||
}
|
||||
@@ -131,11 +183,38 @@ fn create_simple() {
|
||||
assert_eq!(
|
||||
mutations.edits,
|
||||
[
|
||||
CreateElement { root: 1, tag: "div" },
|
||||
CreateElement { root: 2, tag: "div" },
|
||||
CreateElement { root: 3, tag: "div" },
|
||||
CreateElement { root: 4, tag: "div" },
|
||||
AppendChildren { many: 4 },
|
||||
CreateTemplate { id: 0 },
|
||||
CreateElementTemplate {
|
||||
root: 4503599627370495,
|
||||
tag: "div",
|
||||
locally_static: true,
|
||||
fully_static: true
|
||||
},
|
||||
AppendChildren { many: 0 },
|
||||
CreateElementTemplate {
|
||||
root: 4503599627370496,
|
||||
tag: "div",
|
||||
locally_static: true,
|
||||
fully_static: true
|
||||
},
|
||||
AppendChildren { many: 0 },
|
||||
CreateElementTemplate {
|
||||
root: 4503599627370497,
|
||||
tag: "div",
|
||||
locally_static: true,
|
||||
fully_static: true
|
||||
},
|
||||
AppendChildren { many: 0 },
|
||||
CreateElementTemplate {
|
||||
root: 4503599627370498,
|
||||
tag: "div",
|
||||
locally_static: true,
|
||||
fully_static: true
|
||||
},
|
||||
AppendChildren { many: 0 },
|
||||
FinishTemplate { len: 4 },
|
||||
CreateTemplateRef { id: 1, template_id: 0 },
|
||||
AppendChildren { many: 1 }
|
||||
]
|
||||
);
|
||||
}
|
||||
@@ -168,25 +247,50 @@ fn create_components() {
|
||||
assert_eq!(
|
||||
mutations.edits,
|
||||
[
|
||||
CreateElement { root: 1, tag: "h1" },
|
||||
CreateElement { root: 2, tag: "div" },
|
||||
CreateTextNode { root: 3, text: "abc1" },
|
||||
CreateTemplate { id: 0 },
|
||||
CreateElementTemplate {
|
||||
root: 4503599627370495,
|
||||
tag: "h1",
|
||||
locally_static: true,
|
||||
fully_static: true
|
||||
},
|
||||
AppendChildren { many: 0 },
|
||||
CreateElementTemplate {
|
||||
root: 4503599627370496,
|
||||
tag: "div",
|
||||
locally_static: true,
|
||||
fully_static: false
|
||||
},
|
||||
CreatePlaceholderTemplate { root: 4503599627370497 },
|
||||
AppendChildren { many: 1 },
|
||||
CreateElement { root: 4, tag: "p" },
|
||||
CreateElement { root: 5, tag: "h1" },
|
||||
CreateElement { root: 6, tag: "div" },
|
||||
CreateTextNode { root: 7, text: "abc2" },
|
||||
AppendChildren { many: 1 },
|
||||
CreateElement { root: 8, tag: "p" },
|
||||
CreateElement { root: 9, tag: "h1" },
|
||||
CreateElement { root: 10, tag: "div" },
|
||||
CreateTextNode { root: 11, text: "abc3" },
|
||||
AppendChildren { many: 1 },
|
||||
CreateElement { root: 12, tag: "p" },
|
||||
AppendChildren { many: 9 },
|
||||
CreateElementTemplate {
|
||||
root: 4503599627370498,
|
||||
tag: "p",
|
||||
locally_static: true,
|
||||
fully_static: true
|
||||
},
|
||||
AppendChildren { many: 0 },
|
||||
FinishTemplate { len: 3 },
|
||||
CreateTemplateRef { id: 1, template_id: 0 },
|
||||
EnterTemplateRef { root: 1 },
|
||||
CreateTextNode { root: 2, text: "abc1" },
|
||||
ReplaceWith { root: 4503599627370497, m: 1 },
|
||||
ExitTemplateRef {},
|
||||
CreateTemplateRef { id: 3, template_id: 0 },
|
||||
EnterTemplateRef { root: 3 },
|
||||
CreateTextNode { root: 4, text: "abc2" },
|
||||
ReplaceWith { root: 4503599627370497, m: 1 },
|
||||
ExitTemplateRef {},
|
||||
CreateTemplateRef { id: 5, template_id: 0 },
|
||||
EnterTemplateRef { root: 5 },
|
||||
CreateTextNode { root: 6, text: "abc3" },
|
||||
ReplaceWith { root: 4503599627370497, m: 1 },
|
||||
ExitTemplateRef {},
|
||||
AppendChildren { many: 3 }
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn anchors() {
|
||||
static App: Component = |cx| {
|
||||
@@ -201,11 +305,19 @@ fn anchors() {
|
||||
assert_eq!(
|
||||
mutations.edits,
|
||||
[
|
||||
CreateElement { root: 1, tag: "div" },
|
||||
CreateTextNode { root: 2, text: "hello" },
|
||||
CreateTemplate { id: 0 },
|
||||
CreateElementTemplate {
|
||||
root: 4503599627370495,
|
||||
tag: "div",
|
||||
locally_static: true,
|
||||
fully_static: true
|
||||
},
|
||||
CreateTextNodeTemplate { root: 4503599627370496, text: "hello", locally_static: true },
|
||||
AppendChildren { many: 1 },
|
||||
CreatePlaceholder { root: 3 },
|
||||
AppendChildren { many: 2 },
|
||||
FinishTemplate { len: 1 },
|
||||
CreateTemplateRef { id: 1, template_id: 0 },
|
||||
CreatePlaceholder { root: 2 },
|
||||
AppendChildren { many: 2 }
|
||||
]
|
||||
);
|
||||
}
|
||||
@@ -6,10 +6,10 @@
|
||||
//!
|
||||
//! It does not validated that component lifecycles work properly. This is done in another test file.
|
||||
|
||||
use dioxus::prelude::*;
|
||||
use dioxus::{core_macro::rsx_without_templates, prelude::*};
|
||||
|
||||
fn new_dom() -> VirtualDom {
|
||||
VirtualDom::new(|cx| render!("hi"))
|
||||
VirtualDom::new(|cx| cx.render(rsx_without_templates!("hi")))
|
||||
}
|
||||
|
||||
use dioxus_core::DomEdit::*;
|
||||
@@ -19,8 +19,8 @@ use dioxus_core::DomEdit::*;
|
||||
fn html_and_rsx_generate_the_same_output() {
|
||||
let dom = new_dom();
|
||||
let (create, change) = dom.diff_lazynodes(
|
||||
rsx! ( div { "Hello world" } ),
|
||||
rsx! ( div { "Goodbye world" } ),
|
||||
rsx_without_templates! ( div { "Hello world" } ),
|
||||
rsx_without_templates! ( div { "Goodbye world" } ),
|
||||
);
|
||||
assert_eq!(
|
||||
create.edits,
|
||||
@@ -40,7 +40,7 @@ fn html_and_rsx_generate_the_same_output() {
|
||||
fn fragments_create_properly() {
|
||||
let dom = new_dom();
|
||||
|
||||
let create = dom.create_vnodes(rsx! {
|
||||
let create = dom.create_vnodes(rsx_without_templates! {
|
||||
div { "Hello a" }
|
||||
div { "Hello b" }
|
||||
div { "Hello c" }
|
||||
@@ -68,8 +68,8 @@ fn fragments_create_properly() {
|
||||
fn empty_fragments_create_anchors() {
|
||||
let dom = new_dom();
|
||||
|
||||
let left = rsx!({ (0..0).map(|_f| rsx! { div {}}) });
|
||||
let right = rsx!({ (0..1).map(|_f| rsx! { div {}}) });
|
||||
let left = rsx_without_templates!({ (0..0).map(|_f| rsx_without_templates! { div {}}) });
|
||||
let right = rsx_without_templates!({ (0..1).map(|_f| rsx_without_templates! { div {}}) });
|
||||
|
||||
let (create, change) = dom.diff_lazynodes(left, right);
|
||||
|
||||
@@ -91,8 +91,8 @@ fn empty_fragments_create_anchors() {
|
||||
fn empty_fragments_create_many_anchors() {
|
||||
let dom = new_dom();
|
||||
|
||||
let left = rsx!({ (0..0).map(|_f| rsx! { div {}}) });
|
||||
let right = rsx!({ (0..5).map(|_f| rsx! { div {}}) });
|
||||
let left = rsx_without_templates!({ (0..0).map(|_f| rsx_without_templates! { div {}}) });
|
||||
let right = rsx_without_templates!({ (0..5).map(|_f| rsx_without_templates! { div {}}) });
|
||||
|
||||
let (create, change) = dom.diff_lazynodes(left, right);
|
||||
assert_eq!(
|
||||
@@ -119,10 +119,10 @@ fn empty_fragments_create_many_anchors() {
|
||||
fn empty_fragments_create_anchors_with_many_children() {
|
||||
let dom = new_dom();
|
||||
|
||||
let left = rsx!({ (0..0).map(|_| rsx! { div {} }) });
|
||||
let right = rsx!({
|
||||
let left = rsx_without_templates!({ (0..0).map(|_| rsx_without_templates! { div {} }) });
|
||||
let right = rsx_without_templates!({
|
||||
(0..3).map(|f| {
|
||||
rsx! { div { "hello: {f}" }}
|
||||
rsx_without_templates! { div { "hello: {f}" }}
|
||||
})
|
||||
});
|
||||
|
||||
@@ -154,12 +154,12 @@ fn empty_fragments_create_anchors_with_many_children() {
|
||||
fn many_items_become_fragment() {
|
||||
let dom = new_dom();
|
||||
|
||||
let left = rsx!({
|
||||
let left = rsx_without_templates!({
|
||||
(0..2).map(|_| {
|
||||
rsx! { div { "hello" }}
|
||||
rsx_without_templates! { div { "hello" }}
|
||||
})
|
||||
});
|
||||
let right = rsx!({ (0..0).map(|_| rsx! { div {} }) });
|
||||
let right = rsx_without_templates!({ (0..0).map(|_| rsx_without_templates! { div {} }) });
|
||||
|
||||
let (create, change) = dom.diff_lazynodes(left, right);
|
||||
assert_eq!(
|
||||
@@ -190,14 +190,14 @@ fn many_items_become_fragment() {
|
||||
fn two_equal_fragments_are_equal() {
|
||||
let dom = new_dom();
|
||||
|
||||
let left = rsx!({
|
||||
let left = rsx_without_templates!({
|
||||
(0..2).map(|_| {
|
||||
rsx! { div { "hello" }}
|
||||
rsx_without_templates! { div { "hello" }}
|
||||
})
|
||||
});
|
||||
let right = rsx!({
|
||||
let right = rsx_without_templates!({
|
||||
(0..2).map(|_| {
|
||||
rsx! { div { "hello" }}
|
||||
rsx_without_templates! { div { "hello" }}
|
||||
})
|
||||
});
|
||||
|
||||
@@ -210,12 +210,12 @@ fn two_equal_fragments_are_equal() {
|
||||
fn two_fragments_with_differrent_elements_are_differet() {
|
||||
let dom = new_dom();
|
||||
|
||||
let left = rsx!(
|
||||
{ (0..2).map(|_| rsx! { div { }} ) }
|
||||
let left = rsx_without_templates!(
|
||||
{ (0..2).map(|_| rsx_without_templates! { div { }} ) }
|
||||
p {}
|
||||
);
|
||||
let right = rsx!(
|
||||
{ (0..5).map(|_| rsx! (h1 { }) ) }
|
||||
let right = rsx_without_templates!(
|
||||
{ (0..5).map(|_| rsx_without_templates! (h1 { }) ) }
|
||||
p {}
|
||||
);
|
||||
|
||||
@@ -243,12 +243,12 @@ fn two_fragments_with_differrent_elements_are_differet() {
|
||||
fn two_fragments_with_differrent_elements_are_differet_shorter() {
|
||||
let dom = new_dom();
|
||||
|
||||
let left = rsx!(
|
||||
{(0..5).map(|f| {rsx! { div { }}})}
|
||||
let left = rsx_without_templates!(
|
||||
{(0..5).map(|f| {rsx_without_templates! { div { }}})}
|
||||
p {}
|
||||
);
|
||||
let right = rsx!(
|
||||
{(0..2).map(|f| {rsx! { h1 { }}})}
|
||||
let right = rsx_without_templates!(
|
||||
{(0..2).map(|f| {rsx_without_templates! { h1 { }}})}
|
||||
p {}
|
||||
);
|
||||
|
||||
@@ -287,12 +287,12 @@ fn two_fragments_with_differrent_elements_are_differet_shorter() {
|
||||
fn two_fragments_with_same_elements_are_differet() {
|
||||
let dom = new_dom();
|
||||
|
||||
let left = rsx!(
|
||||
{(0..2).map(|f| {rsx! { div { }}})}
|
||||
let left = rsx_without_templates!(
|
||||
{(0..2).map(|f| rsx_without_templates! { div { }})}
|
||||
p {}
|
||||
);
|
||||
let right = rsx!(
|
||||
{(0..5).map(|f| {rsx! { div { }}})}
|
||||
let right = rsx_without_templates!(
|
||||
{(0..5).map(|f| rsx_without_templates! { div { }})}
|
||||
p {}
|
||||
);
|
||||
|
||||
@@ -322,12 +322,12 @@ fn two_fragments_with_same_elements_are_differet() {
|
||||
fn keyed_diffing_order() {
|
||||
let dom = new_dom();
|
||||
|
||||
let left = rsx!(
|
||||
{(0..5).map(|f| {rsx! { div { key: "{f}" }}})}
|
||||
let left = rsx_without_templates!(
|
||||
{(0..5).map(|f| {rsx_without_templates! { div { key: "{f}" }}})}
|
||||
p {"e"}
|
||||
);
|
||||
let right = rsx!(
|
||||
{(0..2).map(|f| rsx! { div { key: "{f}" }})}
|
||||
let right = rsx_without_templates!(
|
||||
{(0..2).map(|f| rsx_without_templates! { div { key: "{f}" }})}
|
||||
p {"e"}
|
||||
);
|
||||
|
||||
@@ -343,15 +343,15 @@ fn keyed_diffing_order() {
|
||||
fn keyed_diffing_out_of_order() {
|
||||
let dom = new_dom();
|
||||
|
||||
let left = rsx!({
|
||||
let left = rsx_without_templates!({
|
||||
[0, 1, 2, 3, /**/ 4, 5, 6, /**/ 7, 8, 9].iter().map(|f| {
|
||||
rsx! { div { key: "{f}" }}
|
||||
rsx_without_templates! { div { key: "{f}" }}
|
||||
})
|
||||
});
|
||||
|
||||
let right = rsx!({
|
||||
let right = rsx_without_templates!({
|
||||
[0, 1, 2, 3, /**/ 6, 4, 5, /**/ 7, 8, 9].iter().map(|f| {
|
||||
rsx! { div { key: "{f}" }}
|
||||
rsx_without_templates! { div { key: "{f}" }}
|
||||
})
|
||||
});
|
||||
|
||||
@@ -368,15 +368,15 @@ fn keyed_diffing_out_of_order() {
|
||||
fn keyed_diffing_out_of_order_adds() {
|
||||
let dom = new_dom();
|
||||
|
||||
let left = rsx!({
|
||||
let left = rsx_without_templates!({
|
||||
[/**/ 4, 5, 6, 7, 8 /**/].iter().map(|f| {
|
||||
rsx! { div { key: "{f}" }}
|
||||
rsx_without_templates! { div { key: "{f}" }}
|
||||
})
|
||||
});
|
||||
|
||||
let right = rsx!({
|
||||
let right = rsx_without_templates!({
|
||||
[/**/ 8, 7, 4, 5, 6 /**/].iter().map(|f| {
|
||||
rsx! { div { key: "{f}" }}
|
||||
rsx_without_templates! { div { key: "{f}" }}
|
||||
})
|
||||
});
|
||||
|
||||
@@ -395,15 +395,15 @@ fn keyed_diffing_out_of_order_adds() {
|
||||
fn keyed_diffing_out_of_order_adds_2() {
|
||||
let dom = new_dom();
|
||||
|
||||
let left = rsx!({
|
||||
let left = rsx_without_templates!({
|
||||
[/**/ 4, 5, 6, 7, 8 /**/].iter().map(|f| {
|
||||
rsx! { div { key: "{f}" }}
|
||||
rsx_without_templates! { div { key: "{f}" }}
|
||||
})
|
||||
});
|
||||
|
||||
let right = rsx!({
|
||||
let right = rsx_without_templates!({
|
||||
[/**/ 7, 8, 4, 5, 6 /**/].iter().map(|f| {
|
||||
rsx! { div { key: "{f}" }}
|
||||
rsx_without_templates! { div { key: "{f}" }}
|
||||
})
|
||||
});
|
||||
|
||||
@@ -423,15 +423,15 @@ fn keyed_diffing_out_of_order_adds_2() {
|
||||
fn keyed_diffing_out_of_order_adds_3() {
|
||||
let dom = new_dom();
|
||||
|
||||
let left = rsx!({
|
||||
let left = rsx_without_templates!({
|
||||
[/**/ 4, 5, 6, 7, 8 /**/].iter().map(|f| {
|
||||
rsx! { div { key: "{f}" }}
|
||||
rsx_without_templates! { div { key: "{f}" }}
|
||||
})
|
||||
});
|
||||
|
||||
let right = rsx!({
|
||||
let right = rsx_without_templates!({
|
||||
[/**/ 4, 8, 7, 5, 6 /**/].iter().map(|f| {
|
||||
rsx! { div { key: "{f}" }}
|
||||
rsx_without_templates! { div { key: "{f}" }}
|
||||
})
|
||||
});
|
||||
|
||||
@@ -451,15 +451,15 @@ fn keyed_diffing_out_of_order_adds_3() {
|
||||
fn keyed_diffing_out_of_order_adds_4() {
|
||||
let dom = new_dom();
|
||||
|
||||
let left = rsx!({
|
||||
let left = rsx_without_templates!({
|
||||
[/**/ 4, 5, 6, 7, 8 /**/].iter().map(|f| {
|
||||
rsx! { div { key: "{f}" }}
|
||||
rsx_without_templates! { div { key: "{f}" }}
|
||||
})
|
||||
});
|
||||
|
||||
let right = rsx!({
|
||||
let right = rsx_without_templates!({
|
||||
[/**/ 4, 5, 8, 7, 6 /**/].iter().map(|f| {
|
||||
rsx! { div { key: "{f}" }}
|
||||
rsx_without_templates! { div { key: "{f}" }}
|
||||
})
|
||||
});
|
||||
|
||||
@@ -479,15 +479,15 @@ fn keyed_diffing_out_of_order_adds_4() {
|
||||
fn keyed_diffing_out_of_order_adds_5() {
|
||||
let dom = new_dom();
|
||||
|
||||
let left = rsx!({
|
||||
let left = rsx_without_templates!({
|
||||
[/**/ 4, 5, 6, 7, 8 /**/].iter().map(|f| {
|
||||
rsx! { div { key: "{f}" }}
|
||||
rsx_without_templates! { div { key: "{f}" }}
|
||||
})
|
||||
});
|
||||
|
||||
let right = rsx!({
|
||||
let right = rsx_without_templates!({
|
||||
[/**/ 4, 5, 6, 8, 7 /**/].iter().map(|f| {
|
||||
rsx! { div { key: "{f}" }}
|
||||
rsx_without_templates! { div { key: "{f}" }}
|
||||
})
|
||||
});
|
||||
|
||||
@@ -502,15 +502,15 @@ fn keyed_diffing_out_of_order_adds_5() {
|
||||
fn keyed_diffing_additions() {
|
||||
let dom = new_dom();
|
||||
|
||||
let left = rsx!({
|
||||
let left = rsx_without_templates!({
|
||||
[/**/ 4, 5, 6, 7, 8 /**/].iter().map(|f| {
|
||||
rsx! { div { key: "{f}" }}
|
||||
rsx_without_templates! { div { key: "{f}" }}
|
||||
})
|
||||
});
|
||||
|
||||
let right = rsx!({
|
||||
let right = rsx_without_templates!({
|
||||
[/**/ 4, 5, 6, 7, 8, 9, 10 /**/].iter().map(|f| {
|
||||
rsx! { div { key: "{f}" }}
|
||||
rsx_without_templates! { div { key: "{f}" }}
|
||||
})
|
||||
});
|
||||
|
||||
@@ -529,15 +529,15 @@ fn keyed_diffing_additions() {
|
||||
fn keyed_diffing_additions_and_moves_on_ends() {
|
||||
let dom = new_dom();
|
||||
|
||||
let left = rsx!({
|
||||
let left = rsx_without_templates!({
|
||||
[/**/ 4, 5, 6, 7 /**/].iter().map(|f| {
|
||||
rsx! { div { key: "{f}" }}
|
||||
rsx_without_templates! { div { key: "{f}" }}
|
||||
})
|
||||
});
|
||||
|
||||
let right = rsx!({
|
||||
let right = rsx_without_templates!({
|
||||
[/**/ 7, 4, 5, 6, 11, 12 /**/].iter().map(|f| {
|
||||
rsx! { div { key: "{f}" }}
|
||||
rsx_without_templates! { div { key: "{f}" }}
|
||||
})
|
||||
});
|
||||
|
||||
@@ -561,15 +561,15 @@ fn keyed_diffing_additions_and_moves_on_ends() {
|
||||
fn keyed_diffing_additions_and_moves_in_middle() {
|
||||
let dom = new_dom();
|
||||
|
||||
let left = rsx!({
|
||||
let left = rsx_without_templates!({
|
||||
[/**/ 1, 2, 3, 4 /**/].iter().map(|f| {
|
||||
rsx! { div { key: "{f}" }}
|
||||
rsx_without_templates! { div { key: "{f}" }}
|
||||
})
|
||||
});
|
||||
|
||||
let right = rsx!({
|
||||
let right = rsx_without_templates!({
|
||||
[/**/ 4, 1, 7, 8, 2, 5, 6, 3 /**/].iter().map(|f| {
|
||||
rsx! { div { key: "{f}" }}
|
||||
rsx_without_templates! { div { key: "{f}" }}
|
||||
})
|
||||
});
|
||||
|
||||
@@ -598,15 +598,15 @@ fn keyed_diffing_additions_and_moves_in_middle() {
|
||||
fn controlled_keyed_diffing_out_of_order() {
|
||||
let dom = new_dom();
|
||||
|
||||
let left = rsx!({
|
||||
let left = rsx_without_templates!({
|
||||
[4, 5, 6, 7].iter().map(|f| {
|
||||
rsx! { div { key: "{f}" }}
|
||||
rsx_without_templates! { div { key: "{f}" }}
|
||||
})
|
||||
});
|
||||
|
||||
let right = rsx!({
|
||||
let right = rsx_without_templates!({
|
||||
[0, 5, 9, 6, 4].iter().map(|f| {
|
||||
rsx! { div { key: "{f}" }}
|
||||
rsx_without_templates! { div { key: "{f}" }}
|
||||
})
|
||||
});
|
||||
|
||||
@@ -636,15 +636,15 @@ fn controlled_keyed_diffing_out_of_order() {
|
||||
fn controlled_keyed_diffing_out_of_order_max_test() {
|
||||
let dom = new_dom();
|
||||
|
||||
let left = rsx!({
|
||||
let left = rsx_without_templates!({
|
||||
[0, 1, 2, 3, 4].iter().map(|f| {
|
||||
rsx! { div { key: "{f}" }}
|
||||
rsx_without_templates! { div { key: "{f}" }}
|
||||
})
|
||||
});
|
||||
|
||||
let right = rsx!({
|
||||
let right = rsx_without_templates!({
|
||||
[3, 0, 1, 10, 2].iter().map(|f| {
|
||||
rsx! { div { key: "{f}" }}
|
||||
rsx_without_templates! { div { key: "{f}" }}
|
||||
})
|
||||
});
|
||||
|
||||
@@ -668,15 +668,15 @@ fn controlled_keyed_diffing_out_of_order_max_test() {
|
||||
fn remove_list() {
|
||||
let dom = new_dom();
|
||||
|
||||
let left = rsx!({
|
||||
let left = rsx_without_templates!({
|
||||
(0..10).rev().take(5).map(|i| {
|
||||
rsx! { Fragment { key: "{i}", "{i}" }}
|
||||
rsx_without_templates! { Fragment { key: "{i}", "{i}" }}
|
||||
})
|
||||
});
|
||||
|
||||
let right = rsx!({
|
||||
let right = rsx_without_templates!({
|
||||
(0..10).rev().take(2).map(|i| {
|
||||
rsx! { Fragment { key: "{i}", "{i}" }}
|
||||
rsx_without_templates! { Fragment { key: "{i}", "{i}" }}
|
||||
})
|
||||
});
|
||||
|
||||
@@ -702,15 +702,15 @@ fn remove_list() {
|
||||
fn remove_list_nokeyed() {
|
||||
let dom = new_dom();
|
||||
|
||||
let left = rsx!({
|
||||
let left = rsx_without_templates!({
|
||||
(0..10).rev().take(5).map(|i| {
|
||||
rsx! { Fragment { "{i}" }}
|
||||
rsx_without_templates! { Fragment { "{i}" }}
|
||||
})
|
||||
});
|
||||
|
||||
let right = rsx!({
|
||||
let right = rsx_without_templates!({
|
||||
(0..10).rev().take(2).map(|i| {
|
||||
rsx! { Fragment { "{i}" }}
|
||||
rsx_without_templates! { Fragment { "{i}" }}
|
||||
})
|
||||
});
|
||||
|
||||
@@ -732,10 +732,10 @@ fn add_nested_elements() {
|
||||
let vdom = new_dom();
|
||||
|
||||
let (_create, change) = vdom.diff_lazynodes(
|
||||
rsx! {
|
||||
rsx_without_templates! {
|
||||
div{}
|
||||
},
|
||||
rsx! {
|
||||
rsx_without_templates! {
|
||||
div{
|
||||
div{}
|
||||
}
|
||||
@@ -758,10 +758,10 @@ fn add_listeners() {
|
||||
let vdom = new_dom();
|
||||
|
||||
let (_create, change) = vdom.diff_lazynodes(
|
||||
rsx! {
|
||||
rsx_without_templates! {
|
||||
div{}
|
||||
},
|
||||
rsx! {
|
||||
rsx_without_templates! {
|
||||
div{
|
||||
onkeyup: |_| {},
|
||||
onkeydown: |_| {},
|
||||
@@ -783,13 +783,13 @@ fn remove_listeners() {
|
||||
let vdom = new_dom();
|
||||
|
||||
let (_create, change) = vdom.diff_lazynodes(
|
||||
rsx! {
|
||||
rsx_without_templates! {
|
||||
div{
|
||||
onkeyup: |_| {},
|
||||
onkeydown: |_| {},
|
||||
}
|
||||
},
|
||||
rsx! {
|
||||
rsx_without_templates! {
|
||||
div{}
|
||||
},
|
||||
);
|
||||
@@ -808,12 +808,12 @@ fn diff_listeners() {
|
||||
let vdom = new_dom();
|
||||
|
||||
let (_create, change) = vdom.diff_lazynodes(
|
||||
rsx! {
|
||||
rsx_without_templates! {
|
||||
div{
|
||||
onkeydown: |_| {},
|
||||
}
|
||||
},
|
||||
rsx! {
|
||||
rsx_without_templates! {
|
||||
div{
|
||||
onkeyup: |_| {},
|
||||
}
|
||||
|
||||
@@ -37,27 +37,37 @@ fn test_early_abort() {
|
||||
assert_eq!(
|
||||
edits.edits,
|
||||
[
|
||||
CreateElement { tag: "div", root: 1 },
|
||||
CreateTextNode { text: "Hello, world!", root: 2 },
|
||||
AppendChildren { many: 1 },
|
||||
CreateTemplate { id: 0 },
|
||||
CreateElementTemplate {
|
||||
root: 4503599627370495,
|
||||
tag: "div",
|
||||
locally_static: true,
|
||||
fully_static: true
|
||||
},
|
||||
CreateTextNodeTemplate {
|
||||
root: 4503599627370496,
|
||||
text: "Hello, world!",
|
||||
locally_static: true
|
||||
},
|
||||
AppendChildren { many: 1 },
|
||||
FinishTemplate { len: 1 },
|
||||
CreateTemplateRef { id: 1, template_id: 0 },
|
||||
AppendChildren { many: 1 }
|
||||
]
|
||||
);
|
||||
|
||||
let edits = dom.hard_diff(ScopeId(0));
|
||||
assert_eq!(
|
||||
edits.edits,
|
||||
[CreatePlaceholder { root: 3 }, ReplaceWith { root: 1, m: 1 },],
|
||||
[CreatePlaceholder { root: 2 }, ReplaceWith { root: 1, m: 1 },],
|
||||
);
|
||||
|
||||
let edits = dom.hard_diff(ScopeId(0));
|
||||
assert_eq!(
|
||||
edits.edits,
|
||||
[
|
||||
CreateElement { tag: "div", root: 1 }, // keys get reused
|
||||
CreateTextNode { text: "Hello, world!", root: 2 }, // keys get reused
|
||||
AppendChildren { many: 1 },
|
||||
ReplaceWith { root: 3, m: 1 },
|
||||
CreateTemplateRef { id: 1, template_id: 0 }, // gets reused
|
||||
ReplaceWith { root: 2, m: 1 }
|
||||
]
|
||||
);
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
//! Tests for the lifecycle of components.
|
||||
use dioxus::prelude::*;
|
||||
use dioxus::{core_macro::rsx_without_templates, prelude::*};
|
||||
use dioxus_core::DomEdit::*;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
@@ -16,7 +16,7 @@ fn manual_diffing() {
|
||||
|
||||
static App: Component<AppProps> = |cx| {
|
||||
let val = cx.props.value.lock().unwrap();
|
||||
cx.render(rsx! { div { "{val}" } })
|
||||
cx.render(rsx_without_templates! { div { "{val}" } })
|
||||
};
|
||||
|
||||
let value = Arc::new(Mutex::new("Hello"));
|
||||
@@ -38,7 +38,7 @@ fn events_generate() {
|
||||
|
||||
let inner = match *count {
|
||||
0 => {
|
||||
rsx! {
|
||||
rsx_without_templates! {
|
||||
div {
|
||||
onclick: move |_| *count += 1,
|
||||
div {
|
||||
@@ -81,21 +81,21 @@ fn components_generate() {
|
||||
*render_phase += 1;
|
||||
|
||||
cx.render(match *render_phase {
|
||||
1 => rsx!("Text0"),
|
||||
2 => rsx!(div {}),
|
||||
3 => rsx!("Text2"),
|
||||
4 => rsx!(Child {}),
|
||||
5 => rsx!({ None as Option<()> }),
|
||||
6 => rsx!("text 3"),
|
||||
7 => rsx!({ (0..2).map(|f| rsx!("text {f}")) }),
|
||||
8 => rsx!(Child {}),
|
||||
1 => rsx_without_templates!("Text0"),
|
||||
2 => rsx_without_templates!(div {}),
|
||||
3 => rsx_without_templates!("Text2"),
|
||||
4 => rsx_without_templates!(Child {}),
|
||||
5 => rsx_without_templates!({ None as Option<()> }),
|
||||
6 => rsx_without_templates!("text 3"),
|
||||
7 => rsx_without_templates!({ (0..2).map(|f| rsx_without_templates!("text {f}")) }),
|
||||
8 => rsx_without_templates!(Child {}),
|
||||
_ => todo!(),
|
||||
})
|
||||
};
|
||||
|
||||
fn Child(cx: Scope) -> Element {
|
||||
println!("Running child");
|
||||
cx.render(rsx! {
|
||||
cx.render(rsx_without_templates! {
|
||||
h1 {}
|
||||
})
|
||||
}
|
||||
@@ -175,53 +175,53 @@ fn component_swap() {
|
||||
*render_phase += 1;
|
||||
|
||||
cx.render(match *render_phase {
|
||||
0 => rsx!(
|
||||
0 => rsx_without_templates!(
|
||||
div {
|
||||
NavBar {}
|
||||
Dashboard {}
|
||||
}
|
||||
),
|
||||
1 => rsx!(
|
||||
1 => rsx_without_templates!(
|
||||
div {
|
||||
NavBar {}
|
||||
Results {}
|
||||
}
|
||||
),
|
||||
2 => rsx!(
|
||||
2 => rsx_without_templates!(
|
||||
div {
|
||||
NavBar {}
|
||||
Dashboard {}
|
||||
}
|
||||
),
|
||||
3 => rsx!(
|
||||
3 => rsx_without_templates!(
|
||||
div {
|
||||
NavBar {}
|
||||
Results {}
|
||||
}
|
||||
),
|
||||
4 => rsx!(
|
||||
4 => rsx_without_templates!(
|
||||
div {
|
||||
NavBar {}
|
||||
Dashboard {}
|
||||
}
|
||||
),
|
||||
_ => rsx!("blah"),
|
||||
_ => rsx_without_templates!("blah"),
|
||||
})
|
||||
};
|
||||
|
||||
static NavBar: Component = |cx| {
|
||||
println!("running navbar");
|
||||
cx.render(rsx! {
|
||||
cx.render(rsx_without_templates! {
|
||||
h1 {
|
||||
"NavBar"
|
||||
{(0..3).map(|f| rsx!(NavLink {}))}
|
||||
{(0..3).map(|f| rsx_without_templates!(NavLink {}))}
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
static NavLink: Component = |cx| {
|
||||
println!("running navlink");
|
||||
cx.render(rsx! {
|
||||
cx.render(rsx_without_templates! {
|
||||
h1 {
|
||||
"NavLink"
|
||||
}
|
||||
@@ -230,7 +230,7 @@ fn component_swap() {
|
||||
|
||||
static Dashboard: Component = |cx| {
|
||||
println!("running dashboard");
|
||||
cx.render(rsx! {
|
||||
cx.render(rsx_without_templates! {
|
||||
div {
|
||||
"dashboard"
|
||||
}
|
||||
@@ -239,7 +239,7 @@ fn component_swap() {
|
||||
|
||||
static Results: Component = |cx| {
|
||||
println!("running results");
|
||||
cx.render(rsx! {
|
||||
cx.render(rsx_without_templates! {
|
||||
div {
|
||||
"results"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#![allow(unused, non_upper_case_globals, non_snake_case)]
|
||||
|
||||
use dioxus::prelude::*;
|
||||
use dioxus::{core_macro::rsx_without_templates, prelude::*};
|
||||
use dioxus_core::{DomEdit, Mutations, SchedulerMsg, ScopeId};
|
||||
use std::rc::Rc;
|
||||
use DomEdit::*;
|
||||
@@ -11,12 +11,12 @@ fn shared_state_test() {
|
||||
|
||||
static App: Component = |cx| {
|
||||
cx.provide_context(Rc::new(MySharedState("world!")));
|
||||
cx.render(rsx!(Child {}))
|
||||
cx.render(rsx_without_templates!(Child {}))
|
||||
};
|
||||
|
||||
static Child: Component = |cx| {
|
||||
let shared = cx.consume_context::<Rc<MySharedState>>()?;
|
||||
cx.render(rsx!("Hello, {shared.0}"))
|
||||
cx.render(rsx_without_templates!("Hello, {shared.0}"))
|
||||
};
|
||||
|
||||
let mut dom = VirtualDom::new(App);
|
||||
@@ -42,13 +42,13 @@ fn swap_test() {
|
||||
cx.provide_context(Rc::new(MySharedState("world!")));
|
||||
|
||||
let child = match *val % 2 {
|
||||
0 => rsx!(
|
||||
0 => rsx_without_templates!(
|
||||
Child1 {
|
||||
Child1 { }
|
||||
Child2 { }
|
||||
}
|
||||
),
|
||||
_ => rsx!(
|
||||
_ => rsx_without_templates!(
|
||||
Child2 {
|
||||
Child2 { }
|
||||
Child2 { }
|
||||
@@ -56,7 +56,7 @@ fn swap_test() {
|
||||
),
|
||||
};
|
||||
|
||||
cx.render(rsx!(
|
||||
cx.render(rsx_without_templates!(
|
||||
Router {
|
||||
div { child }
|
||||
}
|
||||
@@ -65,14 +65,14 @@ fn swap_test() {
|
||||
|
||||
#[inline_props]
|
||||
fn Router<'a>(cx: Scope, children: Element<'a>) -> Element<'a> {
|
||||
cx.render(rsx!(div { children }))
|
||||
cx.render(rsx_without_templates!(div { children }))
|
||||
}
|
||||
|
||||
#[inline_props]
|
||||
fn Child1<'a>(cx: Scope, children: Element<'a>) -> Element {
|
||||
let shared = cx.consume_context::<Rc<MySharedState>>().unwrap();
|
||||
println!("Child1: {}", shared.0);
|
||||
cx.render(rsx! {
|
||||
cx.render(rsx_without_templates! {
|
||||
div {
|
||||
"{shared.0}",
|
||||
children
|
||||
@@ -84,7 +84,7 @@ fn swap_test() {
|
||||
fn Child2<'a>(cx: Scope, children: Element<'a>) -> Element {
|
||||
let shared = cx.consume_context::<Rc<MySharedState>>().unwrap();
|
||||
println!("Child2: {}", shared.0);
|
||||
cx.render(rsx! {
|
||||
cx.render(rsx_without_templates! {
|
||||
h1 {
|
||||
"{shared.0}",
|
||||
children
|
||||
|
||||
@@ -63,14 +63,38 @@ fn conditional_rendering() {
|
||||
assert_eq!(
|
||||
mutations.edits,
|
||||
[
|
||||
CreateElement { root: 1, tag: "h1" },
|
||||
CreateTextNode { root: 2, text: "hello" },
|
||||
CreateTemplate { id: 0 },
|
||||
CreateElementTemplate {
|
||||
root: 4503599627370495,
|
||||
tag: "h1",
|
||||
locally_static: true,
|
||||
fully_static: true
|
||||
},
|
||||
CreateTextNodeTemplate { root: 4503599627370496, text: "hello", locally_static: true },
|
||||
AppendChildren { many: 1 },
|
||||
CreateElement { root: 3, tag: "span" },
|
||||
CreateTextNode { root: 4, text: "a" },
|
||||
CreatePlaceholderTemplate { root: 4503599627370497 },
|
||||
CreatePlaceholderTemplate { root: 4503599627370498 },
|
||||
FinishTemplate { len: 3 },
|
||||
CreateTemplateRef { id: 1, template_id: 0 },
|
||||
EnterTemplateRef { root: 1 },
|
||||
CreateTemplate { id: 1 },
|
||||
CreateElementTemplate {
|
||||
root: 4503599627370495,
|
||||
tag: "span",
|
||||
locally_static: true,
|
||||
fully_static: true
|
||||
},
|
||||
CreateTextNodeTemplate { root: 4503599627370496, text: "a", locally_static: true },
|
||||
AppendChildren { many: 1 },
|
||||
CreatePlaceholder { root: 5 },
|
||||
AppendChildren { many: 3 },
|
||||
FinishTemplate { len: 1 },
|
||||
CreateTemplateRef { id: 2, template_id: 1 },
|
||||
ReplaceWith { root: 4503599627370497, m: 1 },
|
||||
ExitTemplateRef {},
|
||||
EnterTemplateRef { root: 1 },
|
||||
CreatePlaceholder { root: 3 },
|
||||
ReplaceWith { root: 4503599627370498, m: 1 },
|
||||
ExitTemplateRef {},
|
||||
AppendChildren { many: 1 }
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user