mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-10 22:54:12 +00:00
wip: more work on diffing machine
This commit is contained in:
parent
ca3b80f069
commit
9813f23cdf
9 changed files with 172 additions and 299 deletions
|
@ -55,7 +55,7 @@ members = [
|
|||
"packages/core-macro",
|
||||
"packages/core",
|
||||
"packages/html-namespace",
|
||||
# "packages/web",
|
||||
"packages/web",
|
||||
# "packages/cli",
|
||||
# "packages/atoms",
|
||||
# "packages/ssr",
|
||||
|
|
|
@ -4,15 +4,15 @@
|
|||
//! Notice:
|
||||
//! ------
|
||||
//!
|
||||
//! The inspiration and code for this module was originally taken from Dodrio (@fitzgen) and modified to support Components,
|
||||
//! Fragments, Suspense, and additional batching operations.
|
||||
//! The inspiration and code for this module was originally taken from Dodrio (@fitzgen) and then modified to support
|
||||
//! Components, Fragments, Suspense, and additional batching operations.
|
||||
//!
|
||||
//! Implementation Details:
|
||||
//! -----------------------
|
||||
//!
|
||||
//! 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
|
||||
//! to batch nodes if it is more performant for their application. The u32 should be a no-op to hash,
|
||||
//! 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. We care about an ID size of u32
|
||||
//!
|
||||
//!
|
||||
//! Further Reading and Thoughts
|
||||
|
@ -58,6 +58,8 @@ pub trait RealDom {
|
|||
fn create_text_node(&mut self, text: &str) -> RealDomNode;
|
||||
fn create_element(&mut self, tag: &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
|
||||
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
|
||||
VNode::Text(_) => {
|
||||
log::debug!("Replacing el with text");
|
||||
self.create(new_node);
|
||||
self.dom.replace_with();
|
||||
}
|
||||
|
@ -167,10 +168,22 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
|||
// New node is actually a sequence of nodes.
|
||||
// We need to replace this one node with a sequence of nodes
|
||||
// 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
|
||||
VNode::Suspended => todo!(),
|
||||
VNode::Suspended { real } => todo!(),
|
||||
},
|
||||
|
||||
// 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
|
||||
|
@ -214,75 +227,74 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
|||
|
||||
// It's also a component
|
||||
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)
|
||||
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
|
||||
.with_scope(scope_id.unwrap(), |scope| {
|
||||
scope.caller = Rc::downgrade(&new.caller)
|
||||
.with_scope(scope_id.unwrap(), |f| {
|
||||
f.run_scope().unwrap();
|
||||
})
|
||||
.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
|
||||
.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
|
||||
}
|
||||
// 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
|
||||
false => {
|
||||
// A new component has shown up! We need to destroy the old node
|
||||
|
||||
// Wipe the old one and plant the new one
|
||||
// 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());
|
||||
// A new component has shown up! We need to destroy the old node
|
||||
|
||||
// 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);
|
||||
}
|
||||
// Wipe the old one and plant the new one
|
||||
// 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
|
||||
let old_scope = old.ass_scope.borrow().as_ref().unwrap().clone();
|
||||
self.destroy_scopes(old_scope);
|
||||
}
|
||||
}
|
||||
VNode::Fragment(_) => todo!(),
|
||||
VNode::Suspended => todo!(),
|
||||
VNode::Suspended { real } => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
VNode::Fragment(old) => {
|
||||
//
|
||||
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
|
||||
VNode::Element(new) => {}
|
||||
VNode::Text(_) => todo!(),
|
||||
VNode::Suspended => todo!(),
|
||||
VNode::Suspended { real } => 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
|
||||
// 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
|
||||
VNode::Suspended => {
|
||||
VNode::Suspended { real: old_real } => {
|
||||
//
|
||||
match new_node {
|
||||
VNode::Suspended => todo!(),
|
||||
VNode::Suspended { real: new_real } => {
|
||||
//
|
||||
}
|
||||
VNode::Element(_) => todo!(),
|
||||
VNode::Text(_) => 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
|
||||
// text content, and finally (3) append the text node to this
|
||||
// parent.
|
||||
//
|
||||
// Notice: this is a web-specific optimization and may be changed in the future
|
||||
//
|
||||
// TODO move over
|
||||
// if children.len() == 1 {
|
||||
// if let VNode::Text(text) = &children[0] {
|
||||
// self.dom.set_text(text.text);
|
||||
|
@ -373,7 +391,7 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
|||
}
|
||||
|
||||
VNode::Component(component) => {
|
||||
self.dom.create_text_node("placeholder for vcomponent");
|
||||
let real_id = self.dom.create_placeholder();
|
||||
|
||||
// let root_id = next_id();
|
||||
// self.dom.save_known_root(root_id);
|
||||
|
@ -426,7 +444,6 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
|||
new_component.run_scope().unwrap();
|
||||
|
||||
// And then run the diff algorithm
|
||||
// todo!();
|
||||
self.diff_node(new_component.old_frame(), new_component.next_frame());
|
||||
|
||||
// Finally, insert this node as a seen node.
|
||||
|
@ -437,21 +454,17 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
|||
VNode::Fragment(frag) => {
|
||||
// create the children directly in the space
|
||||
for child in frag.children {
|
||||
todo!()
|
||||
// self.create(child);
|
||||
// self.dom.append_child();
|
||||
self.create(child);
|
||||
self.dom.append_child();
|
||||
}
|
||||
}
|
||||
|
||||
VNode::Suspended => {
|
||||
todo!("Creation of VNode::Suspended not yet supported")
|
||||
VNode::Suspended { real } => {
|
||||
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> {
|
||||
|
@ -680,29 +693,29 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
|||
// [... parent]
|
||||
//
|
||||
// Upon exiting, the change list stack is in the same state.
|
||||
fn diff_keyed_children(&self, old: &[VNode<'a>], new: &[VNode<'a>]) {
|
||||
todo!();
|
||||
// if cfg!(debug_assertions) {
|
||||
// let mut keys = fxhash::FxHashSet::default();
|
||||
// let mut assert_unique_keys = |children: &[VNode]| {
|
||||
// keys.clear();
|
||||
// for child in children {
|
||||
// let key = child.key();
|
||||
// debug_assert!(
|
||||
// key.is_some(),
|
||||
// "if any sibling is keyed, all siblings must be keyed"
|
||||
// );
|
||||
// keys.insert(key);
|
||||
// }
|
||||
// debug_assert_eq!(
|
||||
// children.len(),
|
||||
// keys.len(),
|
||||
// "keyed siblings must each have a unique key"
|
||||
// );
|
||||
// };
|
||||
// assert_unique_keys(old);
|
||||
// assert_unique_keys(new);
|
||||
// }
|
||||
fn diff_keyed_children(&self, old: &'a [VNode<'a>], new: &'a [VNode<'a>]) {
|
||||
// todo!();
|
||||
if cfg!(debug_assertions) {
|
||||
let mut keys = fxhash::FxHashSet::default();
|
||||
let mut assert_unique_keys = |children: &'a [VNode<'a>]| {
|
||||
keys.clear();
|
||||
for child in children {
|
||||
let key = child.key();
|
||||
debug_assert!(
|
||||
key.is_some(),
|
||||
"if any sibling is keyed, all siblings must be keyed"
|
||||
);
|
||||
keys.insert(key);
|
||||
}
|
||||
debug_assert_eq!(
|
||||
children.len(),
|
||||
keys.len(),
|
||||
"keyed siblings must each have a unique key"
|
||||
);
|
||||
};
|
||||
assert_unique_keys(old);
|
||||
assert_unique_keys(new);
|
||||
}
|
||||
|
||||
// First up, we diff all the nodes with the same key at the beginning of the
|
||||
// children.
|
||||
|
@ -1090,7 +1103,12 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
|||
// [... parent prev_child]
|
||||
// self.dom.go_to_sibling(i);
|
||||
// [... 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);
|
||||
|
||||
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
|
||||
// VNode::Suspended => should_pop = true,
|
||||
VNode::Suspended => todo!(),
|
||||
VNode::Suspended { real } => todo!(),
|
||||
|
||||
// For components, we load their root and push them onto the stack
|
||||
VNode::Component(sc) => {
|
||||
|
@ -1370,7 +1388,7 @@ mod tests {
|
|||
|
||||
// These would represent failing cases.
|
||||
VNode::Fragment(_) => panic!("Found: Fragment"),
|
||||
VNode::Suspended => panic!("Found: Suspended"),
|
||||
VNode::Suspended { real } => panic!("Found: Suspended"),
|
||||
VNode::Component(_) => panic!("Found: Component"),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -611,14 +611,18 @@ where
|
|||
impl<'a> IntoVNode<'a> for () {
|
||||
fn into_vnode(self, cx: &NodeCtx<'a>) -> VNode<'a> {
|
||||
todo!();
|
||||
VNode::Suspended
|
||||
VNode::Suspended {
|
||||
real: Cell::new(RealDomNode::empty()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoVNode<'a> for Option<()> {
|
||||
fn into_vnode(self, cx: &NodeCtx<'a>) -> VNode<'a> {
|
||||
todo!();
|
||||
VNode::Suspended
|
||||
VNode::Suspended {
|
||||
real: Cell::new(RealDomNode::empty()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ pub enum VNode<'src> {
|
|||
/// A "suspended component"
|
||||
/// This is a masqeurade over an underlying future that needs to complete
|
||||
/// 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)
|
||||
Component(&'src VComponent<'src>),
|
||||
|
@ -49,7 +49,7 @@ impl<'a> Clone for VNode<'a> {
|
|||
VNode::Text(old) => VNode::Text(old.clone()),
|
||||
VNode::Fragment(fragment) => VNode::Fragment(fragment),
|
||||
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::Fragment(_) => todo!(),
|
||||
VNode::Suspended => todo!(),
|
||||
VNode::Suspended { real } => todo!(),
|
||||
VNode::Component(_) => todo!(),
|
||||
}
|
||||
}
|
||||
|
@ -142,7 +142,7 @@ impl<'a> VNode<'a> {
|
|||
VNode::Component(c) => c.key,
|
||||
|
||||
// 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::Text(_) => true,
|
||||
VNode::Fragment(_) => false,
|
||||
VNode::Suspended => false,
|
||||
VNode::Suspended { .. } => false,
|
||||
VNode::Component(_) => false,
|
||||
}
|
||||
}
|
||||
|
@ -165,7 +165,7 @@ impl<'a> VNode<'a> {
|
|||
VNode::Element(el) => Some(el.dom_id.get()),
|
||||
VNode::Text(te) => Some(te.dom_id.get()),
|
||||
VNode::Fragment(_) => todo!(),
|
||||
VNode::Suspended => todo!(),
|
||||
VNode::Suspended { .. } => todo!(),
|
||||
VNode::Component(_) => todo!(),
|
||||
}
|
||||
}
|
||||
|
@ -177,7 +177,7 @@ impl Debug for VNode<'_> {
|
|||
VNode::Element(el) => write!(s, "element, {}", el.tag_name),
|
||||
VNode::Text(t) => write!(s, "text, {}", t.text),
|
||||
VNode::Fragment(_) => write!(s, "fragment"),
|
||||
VNode::Suspended => write!(s, "suspended"),
|
||||
VNode::Suspended { .. } => write!(s, "suspended"),
|
||||
VNode::Component(_) => write!(s, "component"),
|
||||
}
|
||||
}
|
||||
|
@ -436,163 +436,3 @@ impl<'a> VFragment<'a> {
|
|||
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"}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::innerlude::*;
|
||||
|
||||
pub struct DebugDom {
|
||||
counter: u32,
|
||||
counter: u64,
|
||||
}
|
||||
impl DebugDom {
|
||||
pub fn new() -> Self {
|
||||
|
@ -33,6 +33,10 @@ impl RealDom for DebugDom {
|
|||
self.counter += 1;
|
||||
RealDomNode::new(self.counter)
|
||||
}
|
||||
fn create_placeholder(&mut self) -> RealDomNode {
|
||||
self.counter += 1;
|
||||
RealDomNode::new(self.counter)
|
||||
}
|
||||
|
||||
fn new_event_listener(
|
||||
&mut self,
|
||||
|
|
|
@ -64,14 +64,19 @@ pub struct VirtualDom {
|
|||
_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)]
|
||||
pub struct RealDomNode(pub u32);
|
||||
pub struct RealDomNode(pub u64);
|
||||
impl RealDomNode {
|
||||
pub fn new(id: u32) -> Self {
|
||||
pub fn new(id: u64) -> Self {
|
||||
Self(id)
|
||||
}
|
||||
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!
|
||||
fn use_context<T: 'static>(&self) -> &'src T {
|
||||
fn use_context<T: 'static>(&self) -> &'src Rc<T> {
|
||||
self.try_use_context().unwrap()
|
||||
}
|
||||
|
||||
|
@ -871,7 +876,9 @@ Any function prefixed with "use" should not be called conditionally.
|
|||
}
|
||||
None => {
|
||||
// we need to register this task
|
||||
VNode::Suspended
|
||||
VNode::Suspended {
|
||||
real: Cell::new(RealDomNode::empty()),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ atoms = { path="../atoms" }
|
|||
async-channel = "1.6.1"
|
||||
nohash-hasher = "0.2.0"
|
||||
anyhow = "1.0.41"
|
||||
slotmap = "1.0.3"
|
||||
|
||||
[dependencies.web-sys]
|
||||
version = "0.3.50"
|
||||
|
|
|
@ -36,11 +36,11 @@ static Example: FC<()> = |cx| {
|
|||
"Jack!"
|
||||
</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"
|
||||
onclick={move |_| set_name("jill")}>
|
||||
"Jill!"
|
||||
</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"
|
||||
onclick={move |_| set_name("jill")}>
|
||||
"Jill!"
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
|
|
@ -7,6 +7,7 @@ use dioxus_core::{
|
|||
};
|
||||
use fxhash::FxHashMap;
|
||||
use nohash_hasher::IntMap;
|
||||
use slotmap::{DefaultKey, Key, KeyData};
|
||||
use wasm_bindgen::{closure::Closure, JsCast};
|
||||
use web_sys::{
|
||||
window, Document, Element, Event, HtmlElement, HtmlInputElement, HtmlOptionElement, Node,
|
||||
|
@ -14,7 +15,7 @@ use web_sys::{
|
|||
|
||||
pub struct WebsysDom {
|
||||
pub stack: Stack,
|
||||
nodes: IntMap<u32, Node>,
|
||||
nodes: slotmap::SlotMap<DefaultKey, Node>,
|
||||
document: Document,
|
||||
root: Element,
|
||||
|
||||
|
@ -39,8 +40,6 @@ pub struct WebsysDom {
|
|||
// `ptns` = Percy text node separator
|
||||
// TODO
|
||||
last_node_was_text: bool,
|
||||
|
||||
node_counter: Counter,
|
||||
}
|
||||
impl WebsysDom {
|
||||
pub fn new(root: Element) -> Self {
|
||||
|
@ -52,16 +51,18 @@ impl WebsysDom {
|
|||
let (sender, mut receiver) = async_channel::unbounded::<EventTrigger>();
|
||||
|
||||
let sender_callback = Arc::new(move |ev| {
|
||||
let mut c = sender.clone();
|
||||
let c = sender.clone();
|
||||
wasm_bindgen_futures::spawn_local(async move {
|
||||
c.send(ev).await.unwrap();
|
||||
});
|
||||
});
|
||||
|
||||
let mut nodes =
|
||||
HashMap::with_capacity_and_hasher(1000, nohash_hasher::BuildNoHashHasher::default());
|
||||
let mut nodes = slotmap::SlotMap::new();
|
||||
// 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 {
|
||||
stack: Stack::with_capacity(10),
|
||||
nodes,
|
||||
|
@ -74,7 +75,6 @@ impl WebsysDom {
|
|||
trigger: sender_callback,
|
||||
root,
|
||||
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 {
|
||||
fn push_root(&mut self, root: dioxus_core::virtual_dom::RealDomNode) {
|
||||
log::debug!("Called `[`push_root] {:?}", root);
|
||||
let domnode = self.nodes.get(&root.0).expect("Failed to pop know root");
|
||||
log::debug!("Called [push_root] {:?}", 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());
|
||||
}
|
||||
|
||||
|
@ -166,15 +160,19 @@ impl dioxus_core::diff::RealDom for WebsysDom {
|
|||
todo!()
|
||||
}
|
||||
|
||||
fn create_placeholder(&mut self) -> RealDomNode {
|
||||
self.create_element("pre")
|
||||
}
|
||||
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
|
||||
.document
|
||||
.create_text_node(text)
|
||||
.dyn_into::<Node>()
|
||||
.unwrap();
|
||||
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);
|
||||
|
||||
|
@ -190,8 +188,8 @@ impl dioxus_core::diff::RealDom for WebsysDom {
|
|||
.unwrap();
|
||||
|
||||
self.stack.push(el.clone());
|
||||
let nid = self.node_counter.next();
|
||||
self.nodes.insert(nid, el);
|
||||
// let nid = self.node_counter.?next();
|
||||
let nid = self.nodes.insert(el).data().as_ffi();
|
||||
log::debug!("Called [`create_element`]: {}, {:?}", tag, nid);
|
||||
RealDomNode::new(nid)
|
||||
}
|
||||
|
@ -209,8 +207,9 @@ impl dioxus_core::diff::RealDom for WebsysDom {
|
|||
.unwrap();
|
||||
|
||||
self.stack.push(el.clone());
|
||||
let nid = self.node_counter.next();
|
||||
self.nodes.insert(nid, el);
|
||||
let nid = self.nodes.insert(el).data().as_ffi();
|
||||
// let nid = self.node_counter.next();
|
||||
// self.nodes.insert(nid, el);
|
||||
log::debug!("Called [`create_element_ns`]: {:}", nid);
|
||||
RealDomNode::new(nid)
|
||||
}
|
||||
|
@ -287,7 +286,7 @@ impl dioxus_core::diff::RealDom for WebsysDom {
|
|||
.context("")?;
|
||||
let real_id = fields
|
||||
.next()
|
||||
.and_then(|f| f.parse::<u32>().ok().map(RealDomNode::new))
|
||||
.and_then(|f| f.parse::<u64>().ok().map(RealDomNode::new))
|
||||
.context("")?;
|
||||
|
||||
// Call the trigger
|
||||
|
|
Loading…
Reference in a new issue