wip: it works but the page is backwards

This commit is contained in:
Jonathan Kelley committed 2021-08-24 12:43:46 -04:00
1 parent a0a977a12d
commit cdcd8611e8
19 files changed
+239 -349

No files matched your search

+20 -20
View File
@@ -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::<Element>().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::<Element>().unwrap();
let el = old.dyn_ref::<Element>().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>() {
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
+64 -41
View File
@@ -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<T: Properties + 'static>(
) -> 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<T: Properties + 'static>(
Ok(())
}
struct HydrationNode {
id: usize,
node: Node,
}