wip: more work on diffing machine

This commit is contained in:
Jonathan Kelley committed 2021-06-26 22:13:57 -04:00
1 parent ca3b80f069
commit 9813f23cdf
9 files changed
+172 -299

No files matched your search

+1 -1
View File
@@ -55,7 +55,7 @@ members = [
"packages/core-macro", "packages/core-macro",
"packages/core", "packages/core",
"packages/html-namespace", "packages/html-namespace",
# "packages/web", "packages/web",
# "packages/cli", # "packages/cli",
# "packages/atoms", # "packages/atoms",
# "packages/ssr", # "packages/ssr",
+112 -94
View File
@@ -4,15 +4,15 @@
//! Notice: //! Notice:
//! ------ //! ------
//! //!
//! The inspiration and code for this module was originally taken from Dodrio (@fitzgen) and modified to support Components, //! The inspiration and code for this module was originally taken from Dodrio (@fitzgen) and then modified to support
//! Fragments, Suspense, and additional batching operations. //! Components, Fragments, Suspense, and additional batching operations.
//! //!
//! Implementation Details: //! Implementation Details:
//! ----------------------- //! -----------------------
//! //!
//! All nodes are addressed by their IDs. The RealDom provides an imperative interface for making changes to these nodes. //! All nodes are addressed by their IDs. The RealDom provides an imperative interface for making changes to these nodes.
//! We don't necessarily intend for changes to happen exactly during the diffing process, so the implementor may choose //! We don't necessarily require that DOM changes happen instnatly during the diffing process, so the implementor may choose
//! to batch nodes if it is more performant for their application. The u32 should be a no-op to hash, //! to batch nodes if it is more performant for their application. We care about an ID size of u32
//! //!
//! //!
//! Further Reading and Thoughts //! Further Reading and Thoughts
@@ -58,6 +58,8 @@ pub trait RealDom {
fn create_text_node(&mut self, text: &str) -> RealDomNode; fn create_text_node(&mut self, text: &str) -> RealDomNode;
fn create_element(&mut self, tag: &str) -> RealDomNode; fn create_element(&mut self, tag: &str) -> RealDomNode;
fn create_element_ns(&mut self, tag: &str, namespace: &str) -> RealDomNode; fn create_element_ns(&mut self, tag: &str, namespace: &str) -> RealDomNode;
// placeholders are nodes that don't get rendered but still exist as an "anchor" in the real dom
fn create_placeholder(&mut self) -> RealDomNode;
// events // events
fn new_event_listener( fn new_event_listener(
@@ -152,7 +154,6 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
} }
// New node is a text element, need to replace the element with a simple text node // New node is a text element, need to replace the element with a simple text node
VNode::Text(_) => { VNode::Text(_) => {
log::debug!("Replacing el with text");
self.create(new_node); self.create(new_node);
self.dom.replace_with(); self.dom.replace_with();
} }
@@ -167,10 +168,22 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
// New node is actually a sequence of nodes. // New node is actually a sequence of nodes.
// We need to replace this one node with a sequence of nodes // We need to replace this one node with a sequence of nodes
// Not yet implement because it's kinda hairy // Not yet implement because it's kinda hairy
VNode::Fragment(_) => todo!(), VNode::Fragment(new) => {
match new.children.len() {
0 => {
// remove
}
1 => {
// replace
}
_ => {
// remove and mount many
}
}
}
// New Node is actually suspended. Todo // New Node is actually suspended. Todo
VNode::Suspended => todo!(), VNode::Suspended { real } => todo!(),
}, },
// Old element was text // Old element was text
@@ -200,7 +213,7 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
} }
} }
} }
VNode::Suspended => todo!(), VNode::Suspended { real } => todo!(),
}, },
// Old element was a component // Old element was a component
@@ -214,75 +227,74 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
// It's also a component // It's also a component
VNode::Component(new) => { VNode::Component(new) => {
match old.user_fc == new.user_fc { if old.user_fc == new.user_fc {
// Make sure we're dealing with the same component (by function pointer) // Make sure we're dealing with the same component (by function pointer)
true => {
// Make sure the new component vnode is referencing the right scope id
let scope_id = old.ass_scope.borrow().clone();
*new.ass_scope.borrow_mut() = scope_id;
// make sure the component's caller function is up to date // Make sure the new component vnode is referencing the right scope id
let scope_id = old.ass_scope.borrow().clone();
*new.ass_scope.borrow_mut() = scope_id;
// make sure the component's caller function is up to date
self.components
.with_scope(scope_id.unwrap(), |scope| {
scope.caller = Rc::downgrade(&new.caller)
})
.unwrap();
// React doesn't automatically memoize, but we do.
// The cost is low enough to make it worth checking
let should_render = match old.comparator {
Some(comparator) => comparator(new),
None => true,
};
if should_render {
// // self.dom.commit_traversal();
self.components self.components
.with_scope(scope_id.unwrap(), |scope| { .with_scope(scope_id.unwrap(), |f| {
scope.caller = Rc::downgrade(&new.caller) f.run_scope().unwrap();
}) })
.unwrap(); .unwrap();
// diff_machine.change_list.load_known_root(root_id);
// React doesn't automatically memoize, but we do. // run the scope
// The cost is low enough to make it worth checking //
let should_render = match old.comparator { } else {
Some(comparator) => comparator(new), // Component has memoized itself and doesn't need to be re-rendered.
None => true, // We still need to make sure the child's props are up-to-date.
}; // Don't commit traversal
if should_render {
// // self.dom.commit_traversal();
self.components
.with_scope(scope_id.unwrap(), |f| {
f.run_scope().unwrap();
})
.unwrap();
// diff_machine.change_list.load_known_root(root_id);
// run the scope
//
} else {
// Component has memoized itself and doesn't need to be re-rendered.
// We still need to make sure the child's props are up-to-date.
// Don't commit traversal
}
} }
} else {
// It's an entirely different component // It's an entirely different component
false => {
// A new component has shown up! We need to destroy the old node
// Wipe the old one and plant the new one // A new component has shown up! We need to destroy the old node
// self.dom.commit_traversal();
// self.dom.replace_node_with(old.dom_id, new.dom_id);
// self.create(new_node);
// self.dom.replace_with();
self.create(new_node);
// self.create_and_repalce(new_node, old.mounted_root.get());
// Now we need to remove the old scope and all of its descendents // Wipe the old one and plant the new one
let old_scope = old.ass_scope.borrow().as_ref().unwrap().clone(); // self.dom.commit_traversal();
self.destroy_scopes(old_scope); // self.dom.replace_node_with(old.dom_id, new.dom_id);
} // self.create(new_node);
// self.dom.replace_with();
self.create(new_node);
// self.create_and_repalce(new_node, old.mounted_root.get());
// Now we need to remove the old scope and all of its descendents
let old_scope = old.ass_scope.borrow().as_ref().unwrap().clone();
self.destroy_scopes(old_scope);
} }
} }
VNode::Fragment(_) => todo!(), VNode::Fragment(_) => todo!(),
VNode::Suspended => todo!(), VNode::Suspended { real } => todo!(),
} }
} }
VNode::Fragment(old) => { VNode::Fragment(old) => {
// //
match new_node { match new_node {
VNode::Fragment(_) => todo!(), VNode::Fragment(new) => todo!(),
// going from fragment to element means we're going from many (or potentially none) to one // going from fragment to element means we're going from many (or potentially none) to one
VNode::Element(new) => {} VNode::Element(new) => {}
VNode::Text(_) => todo!(), VNode::Text(_) => todo!(),
VNode::Suspended => todo!(), VNode::Suspended { real } => todo!(),
VNode::Component(_) => todo!(), VNode::Component(_) => todo!(),
} }
} }
@@ -290,10 +302,12 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
// a suspended node will perform a mem-copy of the previous elements until it is ready // a suspended node will perform a mem-copy of the previous elements until it is ready
// this means that event listeners will need to be disabled and removed // this means that event listeners will need to be disabled and removed
// it also means that props will need to disabled - IE if the node "came out of hibernation" any props should be considered outdated // it also means that props will need to disabled - IE if the node "came out of hibernation" any props should be considered outdated
VNode::Suspended => { VNode::Suspended { real: old_real } => {
// //
match new_node { match new_node {
VNode::Suspended => todo!(), VNode::Suspended { real: new_real } => {
//
}
VNode::Element(_) => todo!(), VNode::Element(_) => todo!(),
VNode::Text(_) => todo!(), VNode::Text(_) => todo!(),
VNode::Fragment(_) => todo!(), VNode::Fragment(_) => todo!(),
@@ -354,6 +368,10 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
// to emit three instructions to (1) create a text node, (2) set its // to emit three instructions to (1) create a text node, (2) set its
// text content, and finally (3) append the text node to this // text content, and finally (3) append the text node to this
// parent. // parent.
//
// Notice: this is a web-specific optimization and may be changed in the future
//
// TODO move over
// if children.len() == 1 { // if children.len() == 1 {
// if let VNode::Text(text) = &children[0] { // if let VNode::Text(text) = &children[0] {
// self.dom.set_text(text.text); // self.dom.set_text(text.text);
@@ -373,7 +391,7 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
} }
VNode::Component(component) => { VNode::Component(component) => {
self.dom.create_text_node("placeholder for vcomponent"); let real_id = self.dom.create_placeholder();
// let root_id = next_id(); // let root_id = next_id();
// self.dom.save_known_root(root_id); // self.dom.save_known_root(root_id);
@@ -426,7 +444,6 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
new_component.run_scope().unwrap(); new_component.run_scope().unwrap();
// And then run the diff algorithm // And then run the diff algorithm
// todo!();
self.diff_node(new_component.old_frame(), new_component.next_frame()); self.diff_node(new_component.old_frame(), new_component.next_frame());
// Finally, insert this node as a seen node. // Finally, insert this node as a seen node.
@@ -437,21 +454,17 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
VNode::Fragment(frag) => { VNode::Fragment(frag) => {
// create the children directly in the space // create the children directly in the space
for child in frag.children { for child in frag.children {
todo!() self.create(child);
// self.create(child); self.dom.append_child();
// self.dom.append_child();
} }
} }
VNode::Suspended => { VNode::Suspended { real } => {
todo!("Creation of VNode::Suspended not yet supported") let id = self.dom.create_placeholder();
real.set(id);
} }
} }
} }
fn iter_children(&self, node: &VNode<'a>) -> ChildIterator<'a> {
todo!()
}
} }
impl<'a, Dom: RealDom> DiffMachine<'a, Dom> { impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
@@ -680,29 +693,29 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
// [... parent] // [... parent]
// //
// Upon exiting, the change list stack is in the same state. // Upon exiting, the change list stack is in the same state.
fn diff_keyed_children(&self, old: &[VNode<'a>], new: &[VNode<'a>]) { fn diff_keyed_children(&self, old: &'a [VNode<'a>], new: &'a [VNode<'a>]) {
todo!(); // todo!();
// if cfg!(debug_assertions) { if cfg!(debug_assertions) {
// let mut keys = fxhash::FxHashSet::default(); let mut keys = fxhash::FxHashSet::default();
// let mut assert_unique_keys = |children: &[VNode]| { let mut assert_unique_keys = |children: &'a [VNode<'a>]| {
// keys.clear(); keys.clear();
// for child in children { for child in children {
// let key = child.key(); let key = child.key();
// debug_assert!( debug_assert!(
// key.is_some(), key.is_some(),
// "if any sibling is keyed, all siblings must be keyed" "if any sibling is keyed, all siblings must be keyed"
// ); );
// keys.insert(key); keys.insert(key);
// } }
// debug_assert_eq!( debug_assert_eq!(
// children.len(), children.len(),
// keys.len(), keys.len(),
// "keyed siblings must each have a unique key" "keyed siblings must each have a unique key"
// ); );
// }; };
// assert_unique_keys(old); assert_unique_keys(old);
// assert_unique_keys(new); assert_unique_keys(new);
// } }
// First up, we diff all the nodes with the same key at the beginning of the // First up, we diff all the nodes with the same key at the beginning of the
// children. // children.
@@ -1090,7 +1103,12 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
// [... parent prev_child] // [... parent prev_child]
// self.dom.go_to_sibling(i); // self.dom.go_to_sibling(i);
// [... parent this_child] // [... parent this_child]
self.dom.push_root(old_child.get_mounted_id().unwrap());
let did = old_child.get_mounted_id().unwrap();
if did.0 == 0 {
log::debug!("Root is bad {:#?}", old_child);
}
self.dom.push_root(did);
self.diff_node(old_child, new_child); self.diff_node(old_child, new_child);
let old_id = old_child.get_mounted_id().unwrap(); let old_id = old_child.get_mounted_id().unwrap();
@@ -1270,7 +1288,7 @@ impl<'a> Iterator for ChildIterator<'a> {
// Immediately abort suspended nodes - can't do anything with them yet // Immediately abort suspended nodes - can't do anything with them yet
// VNode::Suspended => should_pop = true, // VNode::Suspended => should_pop = true,
VNode::Suspended => todo!(), VNode::Suspended { real } => todo!(),
// For components, we load their root and push them onto the stack // For components, we load their root and push them onto the stack
VNode::Component(sc) => { VNode::Component(sc) => {
@@ -1370,7 +1388,7 @@ mod tests {
// These would represent failing cases. // These would represent failing cases.
VNode::Fragment(_) => panic!("Found: Fragment"), VNode::Fragment(_) => panic!("Found: Fragment"),
VNode::Suspended => panic!("Found: Suspended"), VNode::Suspended { real } => panic!("Found: Suspended"),
VNode::Component(_) => panic!("Found: Component"), VNode::Component(_) => panic!("Found: Component"),
} }
} }
+6 -2
View File
@@ -611,14 +611,18 @@ where
impl<'a> IntoVNode<'a> for () { impl<'a> IntoVNode<'a> for () {
fn into_vnode(self, cx: &NodeCtx<'a>) -> VNode<'a> { fn into_vnode(self, cx: &NodeCtx<'a>) -> VNode<'a> {
todo!(); todo!();
VNode::Suspended VNode::Suspended {
real: Cell::new(RealDomNode::empty()),
}
} }
} }
impl<'a> IntoVNode<'a> for Option<()> { impl<'a> IntoVNode<'a> for Option<()> {
fn into_vnode(self, cx: &NodeCtx<'a>) -> VNode<'a> { fn into_vnode(self, cx: &NodeCtx<'a>) -> VNode<'a> {
todo!(); todo!();
VNode::Suspended VNode::Suspended {
real: Cell::new(RealDomNode::empty()),
}
} }
} }
+7 -167
View File
@@ -35,7 +35,7 @@ pub enum VNode<'src> {
/// A "suspended component" /// A "suspended component"
/// This is a masqeurade over an underlying future that needs to complete /// This is a masqeurade over an underlying future that needs to complete
/// When the future is completed, the VNode will then trigger a render /// When the future is completed, the VNode will then trigger a render
Suspended, Suspended { real: Cell<RealDomNode> },
/// A User-defined componen node (node type COMPONENT_NODE) /// A User-defined componen node (node type COMPONENT_NODE)
Component(&'src VComponent<'src>), Component(&'src VComponent<'src>),
@@ -49,7 +49,7 @@ impl<'a> Clone for VNode<'a> {
VNode::Text(old) => VNode::Text(old.clone()), VNode::Text(old) => VNode::Text(old.clone()),
VNode::Fragment(fragment) => VNode::Fragment(fragment), VNode::Fragment(fragment) => VNode::Fragment(fragment),
VNode::Component(component) => VNode::Component(component), VNode::Component(component) => VNode::Component(component),
VNode::Suspended => VNode::Suspended, VNode::Suspended { real } => VNode::Suspended { real: real.clone() },
} }
} }
} }
@@ -85,7 +85,7 @@ impl<'old, 'new> VNode<'old> {
} }
VNode::Text(_) => todo!(), VNode::Text(_) => todo!(),
VNode::Fragment(_) => todo!(), VNode::Fragment(_) => todo!(),
VNode::Suspended => todo!(), VNode::Suspended { real } => todo!(),
VNode::Component(_) => todo!(), VNode::Component(_) => todo!(),
} }
} }
@@ -142,7 +142,7 @@ impl<'a> VNode<'a> {
VNode::Component(c) => c.key, VNode::Component(c) => c.key,
// todo suspend should be allowed to have keys // todo suspend should be allowed to have keys
VNode::Suspended => NodeKey::NONE, VNode::Suspended { .. } => NodeKey::NONE,
} }
} }
@@ -155,7 +155,7 @@ impl<'a> VNode<'a> {
VNode::Element(_) => true, VNode::Element(_) => true,
VNode::Text(_) => true, VNode::Text(_) => true,
VNode::Fragment(_) => false, VNode::Fragment(_) => false,
VNode::Suspended => false, VNode::Suspended { .. } => false,
VNode::Component(_) => false, VNode::Component(_) => false,
} }
} }
@@ -165,7 +165,7 @@ impl<'a> VNode<'a> {
VNode::Element(el) => Some(el.dom_id.get()), VNode::Element(el) => Some(el.dom_id.get()),
VNode::Text(te) => Some(te.dom_id.get()), VNode::Text(te) => Some(te.dom_id.get()),
VNode::Fragment(_) => todo!(), VNode::Fragment(_) => todo!(),
VNode::Suspended => todo!(), VNode::Suspended { .. } => todo!(),
VNode::Component(_) => todo!(), VNode::Component(_) => todo!(),
} }
} }
@@ -177,7 +177,7 @@ impl Debug for VNode<'_> {
VNode::Element(el) => write!(s, "element, {}", el.tag_name), VNode::Element(el) => write!(s, "element, {}", el.tag_name),
VNode::Text(t) => write!(s, "text, {}", t.text), VNode::Text(t) => write!(s, "text, {}", t.text),
VNode::Fragment(_) => write!(s, "fragment"), VNode::Fragment(_) => write!(s, "fragment"),
VNode::Suspended => write!(s, "suspended"), VNode::Suspended { .. } => write!(s, "suspended"),
VNode::Component(_) => write!(s, "component"), VNode::Component(_) => write!(s, "component"),
} }
} }
@@ -436,163 +436,3 @@ impl<'a> VFragment<'a> {
Self { key, children } Self { key, children }
} }
} }
// /// This method converts a list of nested real/virtual nodes into a stream of nodes that are definitely associated
// /// with the real dom. The only types of nodes that may be returned are text, elemets, and components.
// ///
// /// Components *are* considered virtual, but this iterator can't necessarily handle them without the scope arena.
// ///
// /// Why?
// /// ---
// /// Fragments are seen as virtual nodes but are actually a list of possibly-real nodes.
// /// JS implementations normalize their node lists when fragments are present. Here, we just create a new iterator
// /// that iterates through the recursive nesting of fragments.
// ///
// /// Fragments are stupid and I wish we didn't need to support them.
// ///
// /// This iterator only supports 3 levels of nested fragments
// ///
// pub fn iterate_real_nodes<'a>(nodes: &'a [VNode<'a>]) -> RealNodeIterator<'a> {
// RealNodeIterator::new(nodes)
// }
// pub struct RealNodeIterator<'a> {
// nodes: &'a [VNode<'a>],
// // this node is always a "real" node
// // the index is "what sibling # is it"
// // IE in a list of children on a fragment, the node will be a text node that's the 5th sibling
// node_stack: Vec<(&'a VNode<'a>, u32)>,
// }
// impl<'a> RealNodeIterator<'a> {
// // We immediately descend to the first real node we can find
// fn new(nodes: &'a [VNode<'a>]) -> Self {
// let mut node_stack = Vec::new();
// if nodes.len() > 0 {
// let mut cur_node = nodes.get(0).unwrap();
// loop {
// node_stack.push((cur_node, 0_u32));
// if !cur_node.is_real() {
// cur_node = cur_node.get_child(0).unwrap();
// } else {
// break;
// }
// }
// }
// Self { nodes, node_stack }
// }
// // // advances the cursor to the next element, panicing if we're on the 3rd level and still finding fragments
// // fn advance_cursor(&mut self) {
// // let (mut cur_node, mut cur_id) = self.node_stack.last().unwrap();
// // while !cur_node.is_real() {
// // match cur_node {
// // VNode::Element(_) | VNode::Text(_) => todo!(),
// // VNode::Suspended => todo!(),
// // VNode::Component(_) => todo!(),
// // VNode::Fragment(frag) => {
// // let p = frag.children;
// // }
// // }
// // }
// // }
// fn next_node(&mut self) -> bool {
// let (mut cur_node, cur_id) = self.node_stack.last_mut().unwrap();
// match cur_node {
// VNode::Fragment(frag) => {
// //
// if *cur_id + 1 > frag.children.len() as u32 {
// self.node_stack.pop();
// let next = self.node_stack.last_mut();
// return false;
// }
// *cur_id += 1;
// true
// }
// VNode::Element(_) => todo!(),
// VNode::Text(_) => todo!(),
// VNode::Suspended => todo!(),
// VNode::Component(_) => todo!(),
// }
// }
// fn get_current_node(&self) -> Option<&VNode<'a>> {
// self.node_stack.last().map(|(node, id)| match node {
// VNode::Element(_) => todo!(),
// VNode::Text(_) => todo!(),
// VNode::Fragment(_) => todo!(),
// VNode::Suspended => todo!(),
// VNode::Component(_) => todo!(),
// })
// }
// }
// impl<'a> Iterator for RealNodeIterator<'a> {
// type Item = &'a VNode<'a>;
// fn next(&mut self) -> Option<Self::Item> {
// todo!()
// // let top_idx = self.nesting_idxs.get_mut(0).unwrap();
// // let node = &self.nodes.get_mut(*top_idx as usize);
// // if node.is_none() {
// // return None;
// // }
// // let node = node.unwrap();
// // match node {
// // VNode::Element(_) | VNode::Text(_) => {
// // *top_idx += 1;
// // return Some(node);
// // }
// // VNode::Suspended => todo!(),
// // // we need access over the scope map
// // VNode::Component(_) => todo!(),
// // VNode::Fragment(frag) => {
// // let nest_idx = self.nesting_idxs.get_mut(1).unwrap();
// // let node = &frag.children.get_mut(*nest_idx as usize);
// // match node {
// // VNode::Element(_) | VNode::Text(_) => {
// // *nest_idx += 1;
// // return Some(node);
// // }
// // VNode::Fragment(_) => todo!(),
// // VNode::Suspended => todo!(),
// // VNode::Component(_) => todo!(),
// // }
// // }
// // }
// }
// }
mod tests {
use crate::debug_renderer::DebugRenderer;
use crate::nodebuilder::LazyNodes;
use crate as dioxus;
use dioxus::prelude::*;
#[test]
fn iterate_nodes() {
let rs = rsx! {
Fragment {
Fragment {
Fragment {
Fragment {
h1 {"abc1"}
}
h2 {"abc2"}
}
h3 {"abc3"}
}
h4 {"abc4"}
}
};
}
}
+5 -1
View File
@@ -1,7 +1,7 @@
use crate::innerlude::*; use crate::innerlude::*;
pub struct DebugDom { pub struct DebugDom {
counter: u32, counter: u64,
} }
impl DebugDom { impl DebugDom {
pub fn new() -> Self { pub fn new() -> Self {
@@ -33,6 +33,10 @@ impl RealDom for DebugDom {
self.counter += 1; self.counter += 1;
RealDomNode::new(self.counter) RealDomNode::new(self.counter)
} }
fn create_placeholder(&mut self) -> RealDomNode {
self.counter += 1;
RealDomNode::new(self.counter)
}
fn new_event_listener( fn new_event_listener(
&mut self, &mut self,
+12 -5
View File
@@ -64,14 +64,19 @@ pub struct VirtualDom {
_root_prop_type: std::any::TypeId, _root_prop_type: std::any::TypeId,
} }
/// The `RealDomNode` is an ID handle that corresponds to a foreign DOM node.
///
/// "u64" was chosen for two reasons
/// - 0 cost hashing
/// - use with slotmap and other versioned slot arenas
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub struct RealDomNode(pub u32); pub struct RealDomNode(pub u64);
impl RealDomNode { impl RealDomNode {
pub fn new(id: u32) -> Self { pub fn new(id: u64) -> Self {
Self(id) Self(id)
} }
pub fn empty() -> Self { pub fn empty() -> Self {
Self(u32::MIN) Self(u64::MIN)
} }
} }
@@ -795,7 +800,7 @@ Any function prefixed with "use" should not be called conditionally.
} }
/// There are hooks going on here! /// There are hooks going on here!
fn use_context<T: 'static>(&self) -> &'src T { fn use_context<T: 'static>(&self) -> &'src Rc<T> {
self.try_use_context().unwrap() self.try_use_context().unwrap()
} }
@@ -871,7 +876,9 @@ Any function prefixed with "use" should not be called conditionally.
} }
None => { None => {
// we need to register this task // we need to register this task
VNode::Suspended VNode::Suspended {
real: Cell::new(RealDomNode::empty()),
}
} }
} }
} }
+1
View File
@@ -26,6 +26,7 @@ atoms = { path="../atoms" }
async-channel = "1.6.1" async-channel = "1.6.1"
nohash-hasher = "0.2.0" nohash-hasher = "0.2.0"
anyhow = "1.0.41" anyhow = "1.0.41"
slotmap = "1.0.3"
[dependencies.web-sys] [dependencies.web-sys]
version = "0.3.50" version = "0.3.50"
+5 -5
View File
@@ -36,11 +36,11 @@ static Example: FC<()> = |cx| {
"Jack!" "Jack!"
</button> </button>
<button <button
class="inline-block py-4 px-8 mr-6 leading-none text-white bg-indigo-600 hover:bg-indigo-900 font-semibold rounded shadow" class="inline-block py-4 px-8 mr-6 leading-none text-white bg-indigo-600 hover:bg-indigo-900 font-semibold rounded shadow"
onclick={move |_| set_name("jill")}> onclick={move |_| set_name("jill")}>
"Jill!" "Jill!"
</button> </button>
</div> </div>
</div> </div>
</section> </section>
+23 -24
View File
@@ -7,6 +7,7 @@ use dioxus_core::{
}; };
use fxhash::FxHashMap; use fxhash::FxHashMap;
use nohash_hasher::IntMap; use nohash_hasher::IntMap;
use slotmap::{DefaultKey, Key, KeyData};
use wasm_bindgen::{closure::Closure, JsCast}; use wasm_bindgen::{closure::Closure, JsCast};
use web_sys::{ use web_sys::{
window, Document, Element, Event, HtmlElement, HtmlInputElement, HtmlOptionElement, Node, window, Document, Element, Event, HtmlElement, HtmlInputElement, HtmlOptionElement, Node,
@@ -14,7 +15,7 @@ use web_sys::{
pub struct WebsysDom { pub struct WebsysDom {
pub stack: Stack, pub stack: Stack,
nodes: IntMap<u32, Node>, nodes: slotmap::SlotMap<DefaultKey, Node>,
document: Document, document: Document,
root: Element, root: Element,
@@ -39,8 +40,6 @@ pub struct WebsysDom {
// `ptns` = Percy text node separator // `ptns` = Percy text node separator
// TODO // TODO
last_node_was_text: bool, last_node_was_text: bool,
node_counter: Counter,
} }
impl WebsysDom { impl WebsysDom {
pub fn new(root: Element) -> Self { pub fn new(root: Element) -> Self {
@@ -52,16 +51,18 @@ impl WebsysDom {
let (sender, mut receiver) = async_channel::unbounded::<EventTrigger>(); let (sender, mut receiver) = async_channel::unbounded::<EventTrigger>();
let sender_callback = Arc::new(move |ev| { let sender_callback = Arc::new(move |ev| {
let mut c = sender.clone(); let c = sender.clone();
wasm_bindgen_futures::spawn_local(async move { wasm_bindgen_futures::spawn_local(async move {
c.send(ev).await.unwrap(); c.send(ev).await.unwrap();
}); });
}); });
let mut nodes = let mut nodes = slotmap::SlotMap::new();
HashMap::with_capacity_and_hasher(1000, nohash_hasher::BuildNoHashHasher::default()); // HashMap::with_capacity_and_hasher(1000, nohash_hasher::BuildNoHashHasher::default());
// let mut nodes =
// HashMap::with_capacity_and_hasher(1000, nohash_hasher::BuildNoHashHasher::default());
nodes.insert(0_u32, root.clone().dyn_into::<Node>().unwrap()); let root_id = nodes.insert(root.clone().dyn_into::<Node>().unwrap());
Self { Self {
stack: Stack::with_capacity(10), stack: Stack::with_capacity(10),
nodes, nodes,
@@ -74,7 +75,6 @@ impl WebsysDom {
trigger: sender_callback, trigger: sender_callback,
root, root,
last_node_was_text: false, last_node_was_text: false,
node_counter: Counter(0),
} }
} }
@@ -84,17 +84,11 @@ impl WebsysDom {
} }
} }
struct Counter(u32);
impl Counter {
fn next(&mut self) -> u32 {
self.0 += 1;
self.0
}
}
impl dioxus_core::diff::RealDom for WebsysDom { impl dioxus_core::diff::RealDom for WebsysDom {
fn push_root(&mut self, root: dioxus_core::virtual_dom::RealDomNode) { fn push_root(&mut self, root: dioxus_core::virtual_dom::RealDomNode) {
log::debug!("Called `[`push_root] {:?}", root); log::debug!("Called [push_root] {:?}", root);
let domnode = self.nodes.get(&root.0).expect("Failed to pop know root"); let key: DefaultKey = KeyData::from_ffi(root.0).into();
let domnode = self.nodes.get(key).expect("Failed to pop know root");
self.stack.push(domnode.clone()); self.stack.push(domnode.clone());
} }
@@ -166,15 +160,19 @@ impl dioxus_core::diff::RealDom for WebsysDom {
todo!() todo!()
} }
fn create_placeholder(&mut self) -> RealDomNode {
self.create_element("pre")
}
fn create_text_node(&mut self, text: &str) -> dioxus_core::virtual_dom::RealDomNode { fn create_text_node(&mut self, text: &str) -> dioxus_core::virtual_dom::RealDomNode {
let nid = self.node_counter.next(); // let nid = self.node_counter.next();
let textnode = self let textnode = self
.document .document
.create_text_node(text) .create_text_node(text)
.dyn_into::<Node>() .dyn_into::<Node>()
.unwrap(); .unwrap();
self.stack.push(textnode.clone()); self.stack.push(textnode.clone());
self.nodes.insert(nid, textnode); let nid = self.nodes.insert(textnode);
let nid = nid.data().as_ffi();
log::debug!("Called [`create_text_node`]: {}, {}", text, nid); log::debug!("Called [`create_text_node`]: {}, {}", text, nid);
@@ -190,8 +188,8 @@ impl dioxus_core::diff::RealDom for WebsysDom {
.unwrap(); .unwrap();
self.stack.push(el.clone()); self.stack.push(el.clone());
let nid = self.node_counter.next(); // let nid = self.node_counter.?next();
self.nodes.insert(nid, el); let nid = self.nodes.insert(el).data().as_ffi();
log::debug!("Called [`create_element`]: {}, {:?}", tag, nid); log::debug!("Called [`create_element`]: {}, {:?}", tag, nid);
RealDomNode::new(nid) RealDomNode::new(nid)
} }
@@ -209,8 +207,9 @@ impl dioxus_core::diff::RealDom for WebsysDom {
.unwrap(); .unwrap();
self.stack.push(el.clone()); self.stack.push(el.clone());
let nid = self.node_counter.next(); let nid = self.nodes.insert(el).data().as_ffi();
self.nodes.insert(nid, el); // let nid = self.node_counter.next();
// self.nodes.insert(nid, el);
log::debug!("Called [`create_element_ns`]: {:}", nid); log::debug!("Called [`create_element_ns`]: {:}", nid);
RealDomNode::new(nid) RealDomNode::new(nid)
} }
@@ -287,7 +286,7 @@ impl dioxus_core::diff::RealDom for WebsysDom {
.context("")?; .context("")?;
let real_id = fields let real_id = fields
.next() .next()
.and_then(|f| f.parse::<u32>().ok().map(RealDomNode::new)) .and_then(|f| f.parse::<u64>().ok().map(RealDomNode::new))
.context("")?; .context("")?;
// Call the trigger // Call the trigger