-//
-// // Subnodes can even be suspended
-// // When completely rendered, they won't cause the component itself to re-render, just their slot
-
-// //
{ product_list }
-//
-// })
-// }
-
-// fn BuilderComp<'a>(cx: Context<'a, Props>) -> DomTree<'a> {
-// // VNodes can be constructed via a builder or the html! macro
-// // However, both of these are "lazy" - they need to be evaluated (aka, "viewed")
-// // We can "view" them with Context for ultimate speed while inside components
-// cx.render(|bump| {
-// div(bump)
-// .attr("class", "edit")
-// .child(text("Hello"))
-// .child(text(cx.cx.text.as_ref()))
-// .finish()
-// })
-// }
-
-// #[fc]
-// fn EffcComp(cx: Context, name: &str) -> DomTree {
-// // VNodes can be constructed via a builder or the html! macro
-// // However, both of these are "lazy" - they need to be evaluated (aka, "viewed")
-// // We can "view" them with Context for ultimate speed while inside components
-// // use "phase" style allocation;
-
-// cx.render(html! {
-//
-// // your template goes here
-// // feel free to directly use "name"
-//
-// })
-// }
-
-// fn FullySuspended<'a>(cx: &'a Context) -> DomTree<'a> {
-// cx.suspend(async {
-// let i: i32 = 0;
-
-// // full suspended works great with just returning VNodes!
-// // Feel free to capture the html! macro directly
-// // Anything returned here is automatically viewed
-// let tex = match i {
-// 1 => html! {
},
-// 2 => html! {
},
-// _ => html! {
},
-// };
-
-// if cx.cx.blah {
-// html! {
}
-// } else {
-// tex
-// }
-// })
-// }
-
-// /// An example of a datafetching service
-// async fn fetch_data() -> Result {
-// todo!()
-// }
diff --git a/packages/core/src/diff.rs b/packages/core/src/diff.rs
index 539c6986f..0702830a3 100644
--- a/packages/core/src/diff.rs
+++ b/packages/core/src/diff.rs
@@ -89,7 +89,6 @@
//! - https://hacks.mozilla.org/2019/03/fast-bump-allocated-virtual-doms-with-rust-and-wasm/
use crate::{arena::SharedResources, innerlude::*};
-use futures_util::{Future, FutureExt};
use fxhash::{FxHashMap, FxHashSet};
use DomEdit::*;
@@ -281,19 +280,20 @@ impl<'bump> DiffMachine<'bump> {
} = element;
let real_id = self.vdom.reserve_node();
+ dom_id.set(Some(real_id));
+
self.mutations.create_element(tag_name, *namespace, real_id);
self.stack.add_child_count(1);
- dom_id.set(Some(real_id));
-
- let cur_scope = self.stack.current_scope().unwrap();
+ let cur_scope_id = self.stack.current_scope().unwrap();
+ let scope = self.vdom.get_scope(cur_scope_id).unwrap();
listeners.iter().for_each(|listener| {
- self.fix_listener(listener);
+ self.attach_listener_to_scope(listener, scope);
listener.mounted_node.set(Some(real_id));
self.mutations
- .new_event_listener(listener, cur_scope.clone());
+ .new_event_listener(listener, cur_scope_id.clone());
});
for attr in *attributes {
@@ -466,16 +466,19 @@ impl<'bump> DiffMachine<'bump> {
// We also need to make sure that all listeners are properly attached to the parent scope (fix_listener)
//
// TODO: take a more efficient path than this
- let cur_scope = self.stack.current_scope().unwrap();
+
+ let cur_scope_id = self.stack.current_scope().unwrap();
+ let scope = self.vdom.get_scope(cur_scope_id).unwrap();
+
if old.listeners.len() == new.listeners.len() {
for (old_l, new_l) in old.listeners.iter().zip(new.listeners.iter()) {
if old_l.event != new_l.event {
please_commit(&mut self.mutations.edits);
self.mutations.remove_event_listener(old_l.event);
- self.mutations.new_event_listener(new_l, cur_scope);
+ self.mutations.new_event_listener(new_l, cur_scope_id);
}
new_l.mounted_node.set(old_l.mounted_node.get());
- self.fix_listener(new_l);
+ self.attach_listener_to_scope(new_l, scope);
}
} else {
please_commit(&mut self.mutations.edits);
@@ -484,10 +487,8 @@ impl<'bump> DiffMachine<'bump> {
}
for listener in new.listeners {
listener.mounted_node.set(Some(root));
- self.mutations.new_event_listener(listener, cur_scope);
-
- // Make sure the listener gets attached to the scope list
- self.fix_listener(listener);
+ self.mutations.new_event_listener(listener, cur_scope_id);
+ self.attach_listener_to_scope(listener, scope);
}
}
@@ -1082,13 +1083,10 @@ impl<'bump> DiffMachine<'bump> {
}
}
- fn fix_listener<'a>(&mut self, listener: &'a Listener<'a>) {
- let scope_id = self.stack.current_scope();
- if let Some(scope_id) = scope_id {
- let scope = self.vdom.get_scope(scope_id).unwrap();
- let mut queue = scope.listeners.borrow_mut();
- let long_listener: &'a Listener<'static> = unsafe { std::mem::transmute(listener) };
- queue.push(long_listener as *const _)
- }
+ /// Adds a listener closure to a scope during diff.
+ fn attach_listener_to_scope<'a>(&mut self, listener: &'a Listener<'a>, scope: &Scope) {
+ let mut queue = scope.listeners.borrow_mut();
+ let long_listener: &'a Listener<'static> = unsafe { std::mem::transmute(listener) };
+ queue.push(long_listener as *const _)
}
}
diff --git a/packages/core/src/scope.rs b/packages/core/src/scope.rs
index 00b4f251f..61945156a 100644
--- a/packages/core/src/scope.rs
+++ b/packages/core/src/scope.rs
@@ -113,7 +113,6 @@ impl Scope {
child_nodes: ScopeChildren,
) {
self.caller = caller;
- // let child_nodes = unsafe { std::mem::transmute(child_nodes) };
let child_nodes = unsafe { child_nodes.extend_lifetime() };
self.child_nodes = child_nodes;
}
@@ -237,8 +236,8 @@ impl Scope {
}
pub fn consume_garbage(&self) -> Vec<&VNode> {
- let mut garbage = self.pending_garbage.borrow_mut();
- garbage
+ self.pending_garbage
+ .borrow_mut()
.drain(..)
.map(|node| {
// safety: scopes cannot cycle without their garbage being collected. these nodes are safe
diff --git a/packages/core/tests/create_iterative.rs b/packages/core/tests/create_iterative.rs
index 519e97141..cbd4068a0 100644
--- a/packages/core/tests/create_iterative.rs
+++ b/packages/core/tests/create_iterative.rs
@@ -7,6 +7,8 @@ use dioxus_core as dioxus;
use dioxus_html as dioxus_elements;
use DomEdit::*;
+const LOGGING_ENABLED: bool = false;
+
#[test]
fn test_original_diff() {
static App: FC<()> = |cx| {
@@ -57,7 +59,7 @@ async fn create() {
})
};
- test_logging::set_up_logging();
+ test_logging::set_up_logging(LOGGING_ENABLED);
let mut dom = VirtualDom::new(App);
let mutations = dom.rebuild_async().await.unwrap();
assert_eq!(
@@ -98,7 +100,7 @@ async fn create_list() {
})
};
- test_logging::set_up_logging();
+ test_logging::set_up_logging(LOGGING_ENABLED);
let mut dom = VirtualDom::new(App);
let mutations = dom.rebuild_async().await.unwrap();
@@ -141,7 +143,7 @@ async fn create_simple() {
})
};
- test_logging::set_up_logging();
+ test_logging::set_up_logging(LOGGING_ENABLED);
let mut dom = VirtualDom::new(App);
let mutations = dom.rebuild_async().await.unwrap();
@@ -177,7 +179,7 @@ async fn create_components() {
})
};
- test_logging::set_up_logging();
+ test_logging::set_up_logging(LOGGING_ENABLED);
let mut dom = VirtualDom::new(App);
let mutations = dom.rebuild_async().await.unwrap();
@@ -223,7 +225,7 @@ async fn anchors() {
})
};
- test_logging::set_up_logging();
+ test_logging::set_up_logging(LOGGING_ENABLED);
let mut dom = VirtualDom::new(App);
let mutations = dom.rebuild_async().await.unwrap();
@@ -249,7 +251,7 @@ async fn suspended() {
cx.render(rsx! { {val} })
};
- test_logging::set_up_logging();
+ test_logging::set_up_logging(LOGGING_ENABLED);
let mut dom = VirtualDom::new(App);
let mutations = dom.rebuild_async().await.unwrap();
diff --git a/packages/core/tests/diff_iterative.rs b/packages/core/tests/diff_iterative.rs
index f5fba7852..be1c02992 100644
--- a/packages/core/tests/diff_iterative.rs
+++ b/packages/core/tests/diff_iterative.rs
@@ -6,6 +6,8 @@ mod test_logging;
use dioxus_core as dioxus;
use dioxus_html as dioxus_elements;
+const LOGGING_ENABLED: bool = false;
+
#[async_std::test]
async fn test_iterative_create_components() {
static App: FC<()> = |cx| {
@@ -34,7 +36,7 @@ async fn test_iterative_create_components() {
})
}
- test_logging::set_up_logging();
+ test_logging::set_up_logging(LOGGING_ENABLED);
let mut dom = VirtualDom::new(App);
diff --git a/packages/core/tests/diffing.rs b/packages/core/tests/diffing.rs
index b1511c926..bd71bc6d7 100644
--- a/packages/core/tests/diffing.rs
+++ b/packages/core/tests/diffing.rs
@@ -1,12 +1,7 @@
//! Diffing Tests
-//! -------------
-//!
-//! These should always compile and run, but the result is not validat root: (), m: () ed for each test.
-//! TODO: Validate the results beyond visual inspection.
use bumpalo::Bump;
-use anyhow::{Context, Result};
use dioxus::{
arena::SharedResources, diff::DiffMachine, prelude::*, DiffInstruction, DomEdit, MountType,
};
@@ -18,7 +13,7 @@ use DomEdit::*;
// logging is wired up to the test harness
// feel free to enable while debugging
-const IS_LOGGING_ENABLED: bool = true;
+const IS_LOGGING_ENABLED: bool = false;
struct TestDom {
bump: Bump,
@@ -26,9 +21,7 @@ struct TestDom {
}
impl TestDom {
fn new() -> TestDom {
- if IS_LOGGING_ENABLED {
- test_logging::set_up_logging();
- }
+ test_logging::set_up_logging(IS_LOGGING_ENABLED);
let bump = Bump::new();
let resources = SharedResources::new();
TestDom { bump, resources }
@@ -82,8 +75,6 @@ impl TestDom {
let new = self.bump.alloc(self.render(right));
- // let mut create_edits = Vec::new();
-
let mut machine = DiffMachine::new_headless(&self.resources);
machine.stack.create_node(old, MountType::Append);
@@ -343,16 +334,31 @@ fn two_fragments_with_differrent_elements_are_differet() {
let dom = TestDom::new();
let left = rsx!(
- { (0..2).map(|f| rsx! { div { }} ) }
+ { (0..2).map(|_| rsx! { div { }} ) }
p {}
);
let right = rsx!(
- { (0..5).map(|f| rsx! (h1 { }) ) }
+ { (0..5).map(|_| rsx! (h1 { }) ) }
p {}
);
- let edits = dom.lazy_diff(left, right);
- log::debug!("{:#?}", &edits);
+ let (create, changes) = dom.lazy_diff(left, right);
+ log::debug!("{:#?}", &changes);
+ assert_eq!(
+ changes.edits,
+ [
+ // create the new h1s
+ CreateElement { tag: "h1", id: 3 },
+ CreateElement { tag: "h1", id: 4 },
+ CreateElement { tag: "h1", id: 5 },
+ InsertAfter { root: 1, n: 3 },
+ // replace the divs with new h1s
+ CreateElement { tag: "h1", id: 6 },
+ ReplaceWith { root: 0, m: 1 },
+ CreateElement { tag: "h1", id: 7 },
+ ReplaceWith { root: 1, m: 1 },
+ ]
+ );
}
/// Should result in multiple nodes destroyed - with changes to the first nodes
@@ -452,14 +458,6 @@ fn keyed_diffing_order() {
);
}
-#[test]
-fn fragment_keys() {
- let r = 1;
- let p = rsx! {
- Fragment { key: "asd {r}" }
- };
-}
-
/// Should result in moves, but not removals or additions
#[test]
fn keyed_diffing_out_of_order() {
@@ -477,8 +475,12 @@ fn keyed_diffing_out_of_order() {
})
});
- let edits = dom.lazy_diff(left, right);
- log::debug!("{:?}", &edits.1);
+ let (_, changes) = dom.lazy_diff(left, right);
+ log::debug!("{:?}", &changes);
+ assert_eq!(
+ changes.edits,
+ [PushRoot { id: 6 }, InsertBefore { root: 4, n: 1 }]
+ );
}
/// Should result in moves only
@@ -655,21 +657,24 @@ fn keyed_diffing_additions_and_moves_on_ends() {
let right = rsx!({
[/**/ 7, 4, 5, 6, 11, 12 /**/].iter().map(|f| {
- // [/**/ 8, 7, 4, 5, 6, 9, 10 /**/].iter().map(|f| {
rsx! { div { key: "{f}" }}
})
});
let (_, change) = dom.lazy_diff(left, right);
log::debug!("{:?}", change);
- // assert_eq!(
- // change.edits,
- // [
- // CreateElement { id: 5, tag: "div" },
- // CreateElement { id: 6, tag: "div" },
- // InsertAfter { n: 2, root: 4 }
- // ]
- // );
+ assert_eq!(
+ change.edits,
+ [
+ // create 11, 12
+ CreateElement { tag: "div", id: 4 },
+ CreateElement { tag: "div", id: 5 },
+ InsertAfter { root: 2, n: 2 },
+ // move 7 to the front
+ PushRoot { id: 3 },
+ InsertBefore { root: 0, n: 1 }
+ ]
+ );
}
#[test]
@@ -688,16 +693,25 @@ fn keyed_diffing_additions_and_moves_in_middle() {
})
});
+ // LIS: 4, 5, 6
let (_, change) = dom.lazy_diff(left, right);
- log::debug!("{:?}", change);
- // assert_eq!(
- // change.edits,
- // [
- // CreateElement { id: 5, tag: "div" },
- // CreateElement { id: 6, tag: "div" },
- // InsertAfter { n: 2, root: 4 }
- // ]
- // );
+ log::debug!("{:#?}", change);
+ assert_eq!(
+ change.edits,
+ [
+ // create 13, 17
+ CreateElement { tag: "div", id: 4 },
+ CreateElement { tag: "div", id: 5 },
+ InsertBefore { root: 1, n: 2 },
+ // create 11, 12
+ CreateElement { tag: "div", id: 6 },
+ CreateElement { tag: "div", id: 7 },
+ InsertBefore { root: 2, n: 2 },
+ // move 7
+ PushRoot { id: 3 },
+ InsertBefore { root: 0, n: 1 }
+ ]
+ );
}
#[test]
diff --git a/packages/core/tests/test_logging.rs b/packages/core/tests/test_logging.rs
index 41498a9e9..aafb831e2 100644
--- a/packages/core/tests/test_logging.rs
+++ b/packages/core/tests/test_logging.rs
@@ -1,7 +1,11 @@
-pub fn set_up_logging() {
+pub fn set_up_logging(enabled: bool) {
use fern::colors::{Color, ColoredLevelConfig};
use log::debug;
+ if !enabled {
+ return;
+ }
+
// configure colors for the whole line
let colors_line = ColoredLevelConfig::new()
.error(Color::Red)
diff --git a/packages/html/src/attrval.s b/packages/html/src/attrval.rs
similarity index 86%
rename from packages/html/src/attrval.s
rename to packages/html/src/attrval.rs
index cb081f4cb..9ac1f1373 100644
--- a/packages/html/src/attrval.s
+++ b/packages/html/src/attrval.rs
@@ -1,3 +1,6 @@
+//! This module is not included anywhere.
+//!
+//! It is a prototype for a system that supports non-string attribute values.
trait AsAttributeValue: Sized {
fn into_attribute_value<'a>(self, cx: NodeFactory<'a>) -> AttributeValue<'a>;
diff --git a/packages/ssr/examples/basic.rs b/packages/ssr/examples/basic.rs
new file mode 100644
index 000000000..b3bfb6ca5
--- /dev/null
+++ b/packages/ssr/examples/basic.rs
@@ -0,0 +1,25 @@
+use dioxus::virtual_dom::VirtualDom;
+use dioxus_core as dioxus;
+use dioxus_core::prelude::*;
+use dioxus_html as dioxus_elements;
+
+fn main() {
+ let mut dom = VirtualDom::new(App);
+ dom.rebuild().unwrap();
+ println!(
+ "{}",
+ dioxus_ssr::render_vdom(&dom, |c| c.newline(true).indent(true))
+ )
+}
+
+pub static App: FC<()> = |cx| {
+ cx.render(rsx!(
+ div {
+ class: "overflow-hidden"
+ ul {
+ {(0..10).map(|i| rsx!{ li { class: "flex flex-col", "entry: {i}"}})}
+ }
+ "hello world!"
+ }
+ ))
+};
diff --git a/packages/ssr/examples/tofile.rs b/packages/ssr/examples/tofile.rs
index 35ce6db89..09b710486 100644
--- a/packages/ssr/examples/tofile.rs
+++ b/packages/ssr/examples/tofile.rs
@@ -13,7 +13,7 @@ fn main() {
let mut file = File::create("example.html").unwrap();
let mut dom = VirtualDom::new(App);
- dom.rebuild_in_place().expect("failed to run virtualdom");
+ dom.rebuild().expect("failed to run virtualdom");
file.write_fmt(format_args!(
"{}",
diff --git a/packages/ssr/src/lib.rs b/packages/ssr/src/lib.rs
index 80a4c17ef..866acf0a9 100644
--- a/packages/ssr/src/lib.rs
+++ b/packages/ssr/src/lib.rs
@@ -46,7 +46,7 @@ pub fn render_vdom_scope(vdom: &VirtualDom, scope: ScopeId) -> Option {
/// ```ignore
/// static App: FC<()> = |cx| cx.render(rsx!(div { "hello world" }));
/// let mut vdom = VirtualDom::new(App);
-/// vdom.rebuild_in_place();
+/// vdom.rebuild();
///
/// let renderer = TextRenderer::new(&vdom);
/// let output = format!("{}", renderer);
@@ -74,7 +74,7 @@ impl<'a> TextRenderer<'a> {
}
fn html_render(&self, node: &VNode, f: &mut std::fmt::Formatter, il: u16) -> std::fmt::Result {
- match &node.kind {
+ match &node {
VNode::Text(text) => {
if self.cfg.indent {
for _ in 0..il {
@@ -286,28 +286,28 @@ mod tests {
#[test]
fn to_string_works() {
let mut dom = VirtualDom::new(SIMPLE_APP);
- dom.rebuild_in_place().expect("failed to run virtualdom");
+ dom.rebuild().expect("failed to run virtualdom");
dbg!(render_vdom(&dom, |c| c));
}
#[test]
fn hydration() {
let mut dom = VirtualDom::new(NESTED_APP);
- dom.rebuild_in_place().expect("failed to run virtualdom");
+ dom.rebuild().expect("failed to run virtualdom");
dbg!(render_vdom(&dom, |c| c.pre_render(true)));
}
#[test]
fn nested() {
let mut dom = VirtualDom::new(NESTED_APP);
- dom.rebuild_in_place().expect("failed to run virtualdom");
+ dom.rebuild().expect("failed to run virtualdom");
dbg!(render_vdom(&dom, |c| c));
}
#[test]
fn fragment_app() {
let mut dom = VirtualDom::new(FRAGMENT_APP);
- dom.rebuild_in_place().expect("failed to run virtualdom");
+ dom.rebuild().expect("failed to run virtualdom");
dbg!(render_vdom(&dom, |c| c));
}
@@ -319,7 +319,7 @@ mod tests {
let mut file = File::create("index.html").unwrap();
let mut dom = VirtualDom::new(SLIGHTLY_MORE_COMPLEX);
- dom.rebuild_in_place().expect("failed to run virtualdom");
+ dom.rebuild().expect("failed to run virtualdom");
file.write_fmt(format_args!(
"{}",
@@ -337,7 +337,7 @@ mod tests {
};
let mut dom = VirtualDom::new(STLYE_APP);
- dom.rebuild_in_place().expect("failed to run virtualdom");
+ dom.rebuild().expect("failed to run virtualdom");
dbg!(render_vdom(&dom, |c| c));
}
}
diff --git a/packages/web/examples/basic.rs b/packages/web/examples/basic.rs
index 518115f7c..35d82daae 100644
--- a/packages/web/examples/basic.rs
+++ b/packages/web/examples/basic.rs
@@ -25,7 +25,7 @@ fn main() {
}
static App: FC<()> = |cx| {
- let mut count = use_state(cx, || 0);
+ let mut count = use_state(cx, || 3);
cx.render(rsx! {
div {
diff --git a/packages/web/src/dom.rs b/packages/web/src/dom.rs
index e3a4e2c53..2aa57ea5b 100644
--- a/packages/web/src/dom.rs
+++ b/packages/web/src/dom.rs
@@ -84,8 +84,8 @@ impl WebsysDom {
DomEdit::PushRoot { id: root } => self.push(root),
DomEdit::PopRoot => self.pop(),
DomEdit::AppendChildren { many } => self.append_children(many),
- DomEdit::ReplaceWith { n, m } => self.replace_with(n, m),
- DomEdit::Remove => self.remove(),
+ DomEdit::ReplaceWith { m, root } => self.replace_with(m, root),
+ DomEdit::Remove { root } => self.remove(root),
DomEdit::RemoveAllChildren => self.remove_all_children(),
DomEdit::CreateTextNode { text, id } => self.create_text_node(text, id),
DomEdit::CreateElement { tag, id } => self.create_element(tag, None, id),
@@ -102,8 +102,8 @@ impl WebsysDom {
DomEdit::SetAttribute { field, value, ns } => self.set_attribute(field, value, ns),
DomEdit::RemoveAttribute { name } => self.remove_attribute(name),
- DomEdit::InsertAfter { n } => self.insert_after(n),
- DomEdit::InsertBefore { n } => self.insert_before(n),
+ DomEdit::InsertAfter { n, root } => self.insert_after(n, root),
+ DomEdit::InsertBefore { n, root } => self.insert_before(n, root),
}
}
}
@@ -154,7 +154,7 @@ impl WebsysDom {
}
}
- fn replace_with(&mut self, n: u32, m: u32) {
+ fn replace_with(&mut self, m: u32, root: u64) {
log::debug!("Called [`replace_with`]");
let mut new_nodes = vec![];
@@ -162,19 +162,11 @@ impl WebsysDom {
new_nodes.push(self.stack.pop());
}
- let mut old_nodes = vec![];
- for _ in 0..n {
- old_nodes.push(self.stack.pop());
- }
-
- for node in &old_nodes[1..] {
- node.dyn_ref::().unwrap().remove();
- }
-
- let old = old_nodes[0].clone();
+ let old = self.nodes[root as usize].as_ref().unwrap();
let arr: js_sys::Array = new_nodes.iter().collect();
- let el = old.dyn_into::().unwrap();
+ let el = old.dyn_ref::().unwrap();
el.replace_with_with_node(&arr).unwrap();
+
// let arr = js_sys::Array::from();
// TODO: use different-sized replace withs
@@ -205,9 +197,17 @@ impl WebsysDom {
// self.stack.push(new_node);
}
- fn remove(&mut self) {
+ fn remove(&mut self, root: u64) {
log::debug!("Called [`remove`]");
- todo!()
+
+ let node = self.nodes[root as usize].as_ref().unwrap();
+ if let Some(element) = node.dyn_ref::() {
+ element.remove();
+ } else {
+ if let Some(parent) = node.parent_node() {
+ parent.remove_child(&node).unwrap();
+ }
+ }
}
fn remove_all_children(&mut self) {
@@ -361,7 +361,7 @@ impl WebsysDom {
}
}
- fn insert_after(&mut self, n: u32) {
+ fn insert_after(&mut self, n: u32, root: u64) {
let mut new_nodes = vec![];
for _ in 0..n {
new_nodes.push(self.stack.pop());
@@ -380,7 +380,7 @@ impl WebsysDom {
// let el = self.stack.top();
}
- fn insert_before(&mut self, n: u32) {
+ fn insert_before(&mut self, n: u32, root: u64) {
let n = n as usize;
let root = self
.stack
diff --git a/packages/web/src/lib.rs b/packages/web/src/lib.rs
index 9ff8906e9..2adfb6b1c 100644
--- a/packages/web/src/lib.rs
+++ b/packages/web/src/lib.rs
@@ -1,30 +1,56 @@
//! Dioxus WebSys
-//! --------------
-//! This crate implements a renderer of the Dioxus Virtual DOM for the web browser using Websys.
-
-/*
-From Google's guide on rAF and rIC:
---------
-
-If the callback is fired at the end of the frame, it will be scheduled to go after the current frame has been committed,
-which means that style changes will have been applied, and, importantly, layout calculated. If we make DOM changes inside
- of the idle callback, those layout calculations will be invalidated. If there are any kind of layout reads in the next
- frame, e.g. getBoundingClientRect, clientWidth, etc, the browser will have to perform a Forced Synchronous Layout,
- which is a potential performance bottleneck.
-
-Another reason not trigger DOM changes in the idle callback is that the time impact of changing the DOM is unpredictable,
-and as such we could easily go past the deadline the browser provided.
-
-The best practice is to only make DOM changes inside of a requestAnimationFrame callback, since it is scheduled by the
-browser with that type of work in mind. That means that our code will need to use a document fragment, which can then
-be appended in the next requestAnimationFrame callback. If you are using a VDOM library, you would use requestIdleCallback
-to make changes, but you would apply the DOM patches in the next requestAnimationFrame callback, not the idle callback.
-
-Essentially:
-------------
-- Do the VDOM work during the idlecallback
-- Do DOM work in the next requestAnimationFrame callback
-*/
+//!
+//! ## Overview
+//! ------------
+//! This crate implements a renderer of the Dioxus Virtual DOM for the web browser using WebSys. This web render for
+//! Dioxus is one of the more advanced renderers, supporting:
+//! - idle work
+//! - animations
+//! - jank-free rendering
+//! - noderefs
+//! - controlled components
+//! - re-hydration
+//! - and more.
+//!
+//! The actual implementation is farily thin, with the heavy lifting happening inside the Dioxus Core crate.
+//!
+//! To purview the examples, check of the root Dioxus crate - the examples in this crate are mostly meant to provide
+//! validation of websys-specific features and not the general use of Dioxus.
+//!
+//! ## RequestAnimationFrame and RequestIdleCallback
+//! ------------------------------------------------
+//! React implements "jank free rendering" by deliberately not blocking the browser's main thread. For large diffs, long
+//! running work, and integration with things like React-Three-Fiber, it's extremeley important to avoid blocking the
+//! main thread.
+//!
+//! React solves this problem by breaking up the rendering process into a "diff" phase and a "render" phase. In Dioxus,
+//! the diff phase is non-blocking, using "yield_now" to allow the browser to process other events. When the diff phase
+//! is finally complete, the VirtualDOM will return a set of "Mutations" for this crate to apply.
+//!
+//! Here, we schedule the "diff" phase during the browser's idle period, achieved by calling RequestIdleCallback and then
+//! setting a timeout from the that completes when the idleperiod is over. Then, we call requestAnimationFrame
+//!
+//! From Google's guide on rAF and rIC:
+//! -----------------------------------
+//!
+//! If the callback is fired at the end of the frame, it will be scheduled to go after the current frame has been committed,
+//! which means that style changes will have been applied, and, importantly, layout calculated. If we make DOM changes inside
+//! of the idle callback, those layout calculations will be invalidated. If there are any kind of layout reads in the next
+//! frame, e.g. getBoundingClientRect, clientWidth, etc, the browser will have to perform a Forced Synchronous Layout,
+//! which is a potential performance bottleneck.
+//!
+//! Another reason not trigger DOM changes in the idle callback is that the time impact of changing the DOM is unpredictable,
+//! and as such we could easily go past the deadline the browser provided.
+//!
+//! The best practice is to only make DOM changes inside of a requestAnimationFrame callback, since it is scheduled by the
+//! browser with that type of work in mind. That means that our code will need to use a document fragment, which can then
+//! be appended in the next requestAnimationFrame callback. If you are using a VDOM library, you would use requestIdleCallback
+//! to make changes, but you would apply the DOM patches in the next requestAnimationFrame callback, not the idle callback.
+//!
+//! Essentially:
+//! ------------
+//! - Do the VDOM work during the idlecallback
+//! - Do DOM work in the next requestAnimationFrame callback
use std::rc::Rc;
@@ -103,20 +129,22 @@ pub async fn run_with_props(
) -> Result<()> {
let mut dom = VirtualDom::new_with_props(root, root_props);
+ let hydrating = cfg.hydrate;
+
let root_el = load_document().get_element_by_id(&cfg.rootname).unwrap();
-
let tasks = dom.get_event_sender();
+ let sender_callback = Rc::new(move |event| tasks.unbounded_send(event).unwrap());
- // initialize the virtualdom first
- // if cfg.hydrate {
- // dom.rebuild_in_place()?;
- // }
+ let mut websys_dom = dom::WebsysDom::new(root_el, cfg, sender_callback);
- let mut websys_dom = dom::WebsysDom::new(
- root_el,
- cfg,
- Rc::new(move |event| tasks.unbounded_send(event).unwrap()),
- );
+ let mut mutations = dom.rebuild().unwrap();
+ log::info!("Mutations: {:#?}", mutations);
+
+ // hydrating is simply running the dom for a single render. If the page is already written, then the corresponding
+ // ElementIds should already line up because the web_sys dom has already loaded elements with the DioxusID into memory
+ if !hydrating {
+ websys_dom.process_edits(&mut mutations.edits);
+ }
loop {
let deadline = gloo_timers::future::TimeoutFuture::new(16);
@@ -127,8 +155,3 @@ pub async fn run_with_props(
Ok(())
}
-
-struct HydrationNode {
- id: usize,
- node: Node,
-}