mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-27 14:40:44 +00:00
wip: remove old create
This commit is contained in:
parent
64f289a61c
commit
7b068202ce
1 changed files with 144 additions and 280 deletions
|
@ -103,6 +103,11 @@ pub struct DiffMachine<'bump> {
|
|||
|
||||
pub seen_scopes: FxHashSet<ScopeId>,
|
||||
}
|
||||
|
||||
/// The stack instructions we use to diff and create new nodes.
|
||||
///
|
||||
/// Right now, we insert an instruction for every child node we want to create and diff. This can be less efficient than
|
||||
/// a custom iterator type - but this is current easier to implement. In the future, let's try interact with the stack less.
|
||||
pub enum DiffInstruction<'a> {
|
||||
DiffNode {
|
||||
old: &'a VNode<'a>,
|
||||
|
@ -111,6 +116,7 @@ pub enum DiffInstruction<'a> {
|
|||
},
|
||||
|
||||
Append {},
|
||||
|
||||
Replace {
|
||||
with: usize,
|
||||
},
|
||||
|
@ -189,76 +195,14 @@ impl<'bump> DiffMachine<'bump> {
|
|||
}
|
||||
|
||||
DiffInstruction::Create { node, .. } => {
|
||||
// defer to individual functions so the compiler produces better code
|
||||
// large functions tend to be difficult for the compiler to work with
|
||||
match &node {
|
||||
VNode::Text(text) => {
|
||||
let real_id = self.vdom.reserve_node();
|
||||
self.edit_create_text_node(text.text, real_id);
|
||||
text.dom_id.set(Some(real_id));
|
||||
*self.nodes_created_stack.last_mut().unwrap() += 1;
|
||||
}
|
||||
VNode::Suspended(suspended) => {
|
||||
let real_id = self.vdom.reserve_node();
|
||||
self.edit_create_placeholder(real_id);
|
||||
suspended.node.set(Some(real_id));
|
||||
*self.nodes_created_stack.last_mut().unwrap() += 1;
|
||||
}
|
||||
VNode::Anchor(anchor) => {
|
||||
let real_id = self.vdom.reserve_node();
|
||||
self.edit_create_placeholder(real_id);
|
||||
anchor.dom_id.set(Some(real_id));
|
||||
*self.nodes_created_stack.last_mut().unwrap() += 1;
|
||||
}
|
||||
|
||||
VNode::Element(el) => {
|
||||
let VElement {
|
||||
tag_name,
|
||||
listeners,
|
||||
attributes,
|
||||
children,
|
||||
namespace,
|
||||
static_attrs: _,
|
||||
static_children: _,
|
||||
static_listeners: _,
|
||||
dom_id,
|
||||
key,
|
||||
} = el;
|
||||
|
||||
let real_id = self.vdom.reserve_node();
|
||||
self.edit_create_element(tag_name, *namespace, real_id);
|
||||
dom_id.set(Some(real_id));
|
||||
|
||||
let cur_scope = self.current_scope().unwrap();
|
||||
|
||||
listeners.iter().for_each(|listener| {
|
||||
self.fix_listener(listener);
|
||||
listener.mounted_node.set(Some(real_id));
|
||||
self.edit_new_event_listener(listener, cur_scope.clone());
|
||||
|
||||
// if the node has an event listener, then it must be visited ?
|
||||
});
|
||||
|
||||
for attr in *attributes {
|
||||
self.edit_set_attribute(attr);
|
||||
}
|
||||
|
||||
// TODO: the append child edit
|
||||
|
||||
*self.nodes_created_stack.last_mut().unwrap() += 1;
|
||||
|
||||
// push every child onto the stack
|
||||
self.nodes_created_stack.push(0);
|
||||
for child in *children {
|
||||
self.node_stack
|
||||
.push(DiffInstruction::Create { node: child })
|
||||
}
|
||||
}
|
||||
|
||||
VNode::Fragment(frag) => {
|
||||
for node in frag.children {
|
||||
self.node_stack.push(DiffInstruction::Create { node })
|
||||
}
|
||||
}
|
||||
|
||||
VNode::Text(vtext) => self.create_text_node(vtext),
|
||||
VNode::Suspended(suspended) => self.create_suspended_node(suspended),
|
||||
VNode::Anchor(anchor) => self.create_anchor_node(anchor),
|
||||
VNode::Element(element) => self.create_element_node(element),
|
||||
VNode::Fragment(frag) => self.create_fragment_node(frag),
|
||||
VNode::Component(_) => {
|
||||
//
|
||||
}
|
||||
|
@ -270,44 +214,142 @@ impl<'bump> DiffMachine<'bump> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn create_text_node(&mut self) {}
|
||||
fn create_text_node(&mut self, vtext: &'bump VText<'bump>) {
|
||||
let real_id = self.vdom.reserve_node();
|
||||
self.edit_create_text_node(vtext.text, real_id);
|
||||
vtext.dom_id.set(Some(real_id));
|
||||
*self.nodes_created_stack.last_mut().unwrap() += 1;
|
||||
}
|
||||
|
||||
/// Create the new node, pushing instructions on our instruction stack to create any further children
|
||||
///
|
||||
///
|
||||
pub fn create_iterative(&mut self, node: &'bump VNode<'bump>) {
|
||||
match &node {
|
||||
// singles
|
||||
// update the parent
|
||||
VNode::Text(text) => {
|
||||
let real_id = self.vdom.reserve_node();
|
||||
self.edit_create_text_node(text.text, real_id);
|
||||
text.dom_id.set(Some(real_id));
|
||||
*self.nodes_created_stack.last_mut().unwrap() += 1;
|
||||
}
|
||||
VNode::Suspended(suspended) => {
|
||||
let real_id = self.vdom.reserve_node();
|
||||
self.edit_create_placeholder(real_id);
|
||||
suspended.node.set(Some(real_id));
|
||||
*self.nodes_created_stack.last_mut().unwrap() += 1;
|
||||
}
|
||||
VNode::Anchor(anchor) => {
|
||||
let real_id = self.vdom.reserve_node();
|
||||
self.edit_create_placeholder(real_id);
|
||||
anchor.dom_id.set(Some(real_id));
|
||||
*self.nodes_created_stack.last_mut().unwrap() += 1;
|
||||
}
|
||||
fn create_suspended_node(&mut self, suspended: &'bump VSuspended) {
|
||||
let real_id = self.vdom.reserve_node();
|
||||
self.edit_create_placeholder(real_id);
|
||||
suspended.node.set(Some(real_id));
|
||||
*self.nodes_created_stack.last_mut().unwrap() += 1;
|
||||
}
|
||||
|
||||
VNode::Element(el) => {
|
||||
//
|
||||
fn create_anchor_node(&mut self, anchor: &'bump VAnchor) {
|
||||
let real_id = self.vdom.reserve_node();
|
||||
self.edit_create_placeholder(real_id);
|
||||
anchor.dom_id.set(Some(real_id));
|
||||
*self.nodes_created_stack.last_mut().unwrap() += 1;
|
||||
}
|
||||
|
||||
fn create_element_node(&mut self, element: &'bump VElement<'bump>) {
|
||||
let VElement {
|
||||
tag_name,
|
||||
listeners,
|
||||
attributes,
|
||||
children,
|
||||
namespace,
|
||||
static_attrs: _,
|
||||
static_children: _,
|
||||
static_listeners: _,
|
||||
dom_id,
|
||||
key,
|
||||
} = element;
|
||||
|
||||
let real_id = self.vdom.reserve_node();
|
||||
self.edit_create_element(tag_name, *namespace, real_id);
|
||||
dom_id.set(Some(real_id));
|
||||
|
||||
let cur_scope = self.current_scope().unwrap();
|
||||
|
||||
listeners.iter().for_each(|listener| {
|
||||
self.fix_listener(listener);
|
||||
listener.mounted_node.set(Some(real_id));
|
||||
self.edit_new_event_listener(listener, cur_scope.clone());
|
||||
|
||||
// if the node has an event listener, then it must be visited ?
|
||||
});
|
||||
|
||||
for attr in *attributes {
|
||||
self.edit_set_attribute(attr);
|
||||
}
|
||||
|
||||
// TODO: the append child edit
|
||||
|
||||
*self.nodes_created_stack.last_mut().unwrap() += 1;
|
||||
|
||||
// push every child onto the stack
|
||||
self.nodes_created_stack.push(0);
|
||||
for child in *children {
|
||||
self.node_stack
|
||||
.push(DiffInstruction::Create { node: child })
|
||||
}
|
||||
}
|
||||
|
||||
fn create_fragment_node(&mut self, frag: &'bump VFragment<'bump>) {
|
||||
for node in frag.children {
|
||||
self.node_stack.push(DiffInstruction::Create { node })
|
||||
}
|
||||
}
|
||||
|
||||
fn create_component_node(&mut self, vcomponent: &'bump VComponent<'bump>) {
|
||||
let caller = vcomponent.caller.clone();
|
||||
|
||||
let parent_idx = self.scope_stack.last().unwrap().clone();
|
||||
|
||||
// Insert a new scope into our component list
|
||||
let new_idx = self.vdom.insert_scope_with_key(|new_idx| {
|
||||
let parent_scope = self.get_scope(&parent_idx).unwrap();
|
||||
let height = parent_scope.height + 1;
|
||||
Scope::new(
|
||||
caller,
|
||||
new_idx,
|
||||
Some(parent_idx),
|
||||
height,
|
||||
ScopeChildren(vcomponent.children),
|
||||
self.vdom.clone(),
|
||||
)
|
||||
});
|
||||
|
||||
// Actually initialize the caller's slot with the right address
|
||||
vcomponent.ass_scope.set(Some(new_idx));
|
||||
|
||||
if !vcomponent.can_memoize {
|
||||
let cur_scope = self.get_scope_mut(&parent_idx).unwrap();
|
||||
let extended = vcomponent as *const VComponent;
|
||||
let extended: *const VComponent<'static> = unsafe { std::mem::transmute(extended) };
|
||||
cur_scope.borrowed_props.borrow_mut().push(extended);
|
||||
}
|
||||
|
||||
// TODO:
|
||||
// add noderefs to current noderef list Noderefs
|
||||
// add effects to current effect list Effects
|
||||
|
||||
let new_component = self.get_scope_mut(&new_idx).unwrap();
|
||||
|
||||
// Run the scope for one iteration to initialize it
|
||||
match new_component.run_scope() {
|
||||
Ok(_) => {
|
||||
// all good, new nodes exist
|
||||
}
|
||||
VNode::Fragment(frag) => {
|
||||
//
|
||||
}
|
||||
VNode::Component(comp) => {
|
||||
//
|
||||
Err(err) => {
|
||||
// failed to run. this is the first time the component ran, and it failed
|
||||
// we manually set its head node to an empty fragment
|
||||
panic!("failing components not yet implemented");
|
||||
}
|
||||
}
|
||||
|
||||
// Take the node that was just generated from running the component
|
||||
let nextnode = new_component.frames.fin_head();
|
||||
|
||||
// Push the new scope onto the stack
|
||||
self.scope_stack.push(new_idx);
|
||||
|
||||
// Run the creation algorithm with this scope on the stack
|
||||
let meta = self.create_vnode(nextnode);
|
||||
|
||||
// pop the scope off the stack
|
||||
self.scope_stack.pop();
|
||||
|
||||
if meta.added_to_stack == 0 {
|
||||
panic!("Components should *always* generate nodes - even if they fail");
|
||||
}
|
||||
|
||||
// Finally, insert this scope as a seen node.
|
||||
self.seen_scopes.insert(new_idx);
|
||||
}
|
||||
|
||||
pub fn diff_iterative(&mut self, old_node: &'bump VNode<'bump>, new_node: &'bump VNode<'bump>) {
|
||||
|
@ -734,184 +776,6 @@ impl<'bump> DiffMachine<'bump> {
|
|||
}
|
||||
}
|
||||
|
||||
// Emit instructions to create the given virtual node.
|
||||
//
|
||||
// The change list stack may have any shape upon entering this function:
|
||||
//
|
||||
// [...]
|
||||
//
|
||||
// When this function returns, the new node is on top of the change list stack:
|
||||
//
|
||||
// [... node]
|
||||
pub fn create_vnode(&mut self, node: &'bump VNode<'bump>) -> CreateMeta {
|
||||
match &node {
|
||||
VNode::Text(text) => {
|
||||
let real_id = self.vdom.reserve_node();
|
||||
self.edit_create_text_node(text.text, real_id);
|
||||
text.dom_id.set(Some(real_id));
|
||||
CreateMeta::new(text.is_static, 1)
|
||||
}
|
||||
|
||||
VNode::Anchor(anchor) => {
|
||||
let real_id = self.vdom.reserve_node();
|
||||
self.edit_create_placeholder(real_id);
|
||||
anchor.dom_id.set(Some(real_id));
|
||||
CreateMeta::new(false, 1)
|
||||
}
|
||||
|
||||
VNode::Element(el) => {
|
||||
// we have the potential to completely eliminate working on this node in the future(!)
|
||||
//
|
||||
// This can only be done if all of the elements properties (attrs, children, listeners, etc) are static
|
||||
// While creating these things, keep track if we can memoize this element.
|
||||
// At the end, we'll set this flag on the element to skip it
|
||||
let mut is_static: bool = true;
|
||||
|
||||
let VElement {
|
||||
tag_name,
|
||||
listeners,
|
||||
attributes,
|
||||
children,
|
||||
namespace,
|
||||
static_attrs: _,
|
||||
static_children: _,
|
||||
static_listeners: _,
|
||||
dom_id,
|
||||
key,
|
||||
} = el;
|
||||
|
||||
let real_id = self.vdom.reserve_node();
|
||||
self.edit_create_element(tag_name, *namespace, real_id);
|
||||
dom_id.set(Some(real_id));
|
||||
|
||||
let cur_scope = self.current_scope().unwrap();
|
||||
|
||||
listeners.iter().for_each(|listener| {
|
||||
self.fix_listener(listener);
|
||||
listener.mounted_node.set(Some(real_id));
|
||||
self.edit_new_event_listener(listener, cur_scope.clone());
|
||||
|
||||
// if the node has an event listener, then it must be visited ?
|
||||
is_static = false;
|
||||
});
|
||||
|
||||
for attr in *attributes {
|
||||
is_static = is_static && attr.is_static;
|
||||
self.edit_set_attribute(attr);
|
||||
}
|
||||
|
||||
// Fast path: if there is a single text child, it is faster to
|
||||
// create-and-append the text node all at once via setting the
|
||||
// parent's `textContent` in a single change list instruction than
|
||||
// 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 VNodeKind::Text(text) = &children[0] {
|
||||
// self.set_text(text.text);
|
||||
// return CreateMeta::new(is_static, 1);
|
||||
// }
|
||||
// }
|
||||
|
||||
for child in *children {
|
||||
let child_meta = self.create_vnode(child);
|
||||
is_static = is_static && child_meta.is_static;
|
||||
|
||||
// append whatever children were generated by this call
|
||||
self.edit_append_children(child_meta.added_to_stack);
|
||||
}
|
||||
|
||||
CreateMeta::new(is_static, 1)
|
||||
}
|
||||
|
||||
VNode::Component(vcomponent) => {
|
||||
let caller = vcomponent.caller.clone();
|
||||
|
||||
let parent_idx = self.scope_stack.last().unwrap().clone();
|
||||
|
||||
// Insert a new scope into our component list
|
||||
let new_idx = self.vdom.insert_scope_with_key(|new_idx| {
|
||||
let parent_scope = self.get_scope(&parent_idx).unwrap();
|
||||
let height = parent_scope.height + 1;
|
||||
Scope::new(
|
||||
caller,
|
||||
new_idx,
|
||||
Some(parent_idx),
|
||||
height,
|
||||
ScopeChildren(vcomponent.children),
|
||||
self.vdom.clone(),
|
||||
)
|
||||
});
|
||||
|
||||
// Actually initialize the caller's slot with the right address
|
||||
vcomponent.ass_scope.set(Some(new_idx));
|
||||
|
||||
if !vcomponent.can_memoize {
|
||||
let cur_scope = self.get_scope_mut(&parent_idx).unwrap();
|
||||
let extended = *vcomponent as *const VComponent;
|
||||
let extended: *const VComponent<'static> =
|
||||
unsafe { std::mem::transmute(extended) };
|
||||
cur_scope.borrowed_props.borrow_mut().push(extended);
|
||||
}
|
||||
|
||||
// TODO:
|
||||
// add noderefs to current noderef list Noderefs
|
||||
// add effects to current effect list Effects
|
||||
|
||||
let new_component = self.get_scope_mut(&new_idx).unwrap();
|
||||
|
||||
// Run the scope for one iteration to initialize it
|
||||
match new_component.run_scope() {
|
||||
Ok(_) => {
|
||||
// all good, new nodes exist
|
||||
}
|
||||
Err(err) => {
|
||||
// failed to run. this is the first time the component ran, and it failed
|
||||
// we manually set its head node to an empty fragment
|
||||
panic!("failing components not yet implemented");
|
||||
}
|
||||
}
|
||||
|
||||
// Take the node that was just generated from running the component
|
||||
let nextnode = new_component.frames.fin_head();
|
||||
|
||||
// Push the new scope onto the stack
|
||||
self.scope_stack.push(new_idx);
|
||||
|
||||
// Run the creation algorithm with this scope on the stack
|
||||
let meta = self.create_vnode(nextnode);
|
||||
|
||||
// pop the scope off the stack
|
||||
self.scope_stack.pop();
|
||||
|
||||
if meta.added_to_stack == 0 {
|
||||
panic!("Components should *always* generate nodes - even if they fail");
|
||||
}
|
||||
|
||||
// Finally, insert this scope as a seen node.
|
||||
self.seen_scopes.insert(new_idx);
|
||||
|
||||
CreateMeta::new(vcomponent.is_static, meta.added_to_stack)
|
||||
}
|
||||
|
||||
// Fragments are the only nodes that can contain dynamic content (IE through curlies or iterators).
|
||||
// We can never ignore their contents, so the prescence of a fragment indicates that we need always diff them.
|
||||
// Fragments will just put all their nodes onto the stack after creation
|
||||
VNode::Fragment(frag) => self.create_children(frag.children),
|
||||
|
||||
VNode::Suspended(VSuspended { node: real_node }) => {
|
||||
let id = self.vdom.reserve_node();
|
||||
self.edit_create_placeholder(id);
|
||||
real_node.set(Some(id));
|
||||
CreateMeta::new(false, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn create_children(&mut self, children: &'bump [VNode<'bump>]) -> CreateMeta {
|
||||
let mut is_static = true;
|
||||
let mut added_to_stack = 0;
|
||||
|
|
Loading…
Reference in a new issue