separate mount information from VNodes
This commit is contained in:
1 parent
c9bd5a4e6e
commit
aed29b1dec
10 files changed
+1913
-1844
No files matched your search
@@ -0,0 +1,213 @@
|
||||
use std::{
|
||||
cell::RefMut,
|
||||
ops::{Deref, DerefMut},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
any_props::AnyProps,
|
||||
arena::ElementId,
|
||||
innerlude::{
|
||||
DirtyScope, ElementPath, ElementRef, VComponent, VNodeMount, VText, WriteMutations,
|
||||
},
|
||||
nodes::RenderReturn,
|
||||
nodes::{DynamicNode, VNode},
|
||||
scopes::ScopeId,
|
||||
virtual_dom::VirtualDom,
|
||||
Template, TemplateNode,
|
||||
};
|
||||
|
||||
use DynamicNode::*;
|
||||
|
||||
mod component;
|
||||
mod iterator;
|
||||
mod node;
|
||||
|
||||
impl VirtualDom {
|
||||
pub(crate) fn create_children<'a>(
|
||||
&mut self,
|
||||
to: &mut impl WriteMutations,
|
||||
nodes: impl IntoIterator<Item = &'a VNode>,
|
||||
parent: Option<ElementRef>,
|
||||
) -> usize {
|
||||
nodes
|
||||
.into_iter()
|
||||
.map(|child| child.create(self, to, parent.clone()))
|
||||
.sum()
|
||||
}
|
||||
|
||||
fn remove_element_id(&mut self, to: &mut impl WriteMutations, id: ElementId, gen_muts: bool) {
|
||||
if gen_muts {
|
||||
to.remove_node(id);
|
||||
}
|
||||
self.reclaim(id)
|
||||
}
|
||||
|
||||
/// Simply replace a placeholder with a list of nodes
|
||||
fn replace_placeholder<'a>(
|
||||
&mut self,
|
||||
to: &mut impl WriteMutations,
|
||||
placeholder_id: ElementId,
|
||||
r: impl IntoIterator<Item = &'a VNode>,
|
||||
parent: Option<ElementRef>,
|
||||
) {
|
||||
let m = self.create_children(to, r, parent);
|
||||
to.replace_node_with(placeholder_id, m);
|
||||
self.reclaim(placeholder_id);
|
||||
}
|
||||
|
||||
fn nodes_to_placeholder(
|
||||
&mut self,
|
||||
to: &mut impl WriteMutations,
|
||||
mount: &mut VNodeMount,
|
||||
dyn_node_idx: usize,
|
||||
old_nodes: &[VNode],
|
||||
) {
|
||||
// Create the placeholder first, ensuring we get a dedicated ID for the placeholder
|
||||
let placeholder = self.next_element();
|
||||
|
||||
// Set the id of the placeholder
|
||||
mount.mounted_dynamic_nodes[dyn_node_idx] = placeholder.0;
|
||||
|
||||
to.create_placeholder(placeholder);
|
||||
|
||||
self.replace_nodes(to, old_nodes, 1);
|
||||
}
|
||||
|
||||
/// Replace many nodes with a number of nodes on the stack
|
||||
fn replace_nodes(&mut self, to: &mut impl WriteMutations, nodes: &[VNode], m: usize) {
|
||||
// We want to optimize the replace case to use one less mutation if possible
|
||||
// Since mutations are done in reverse, the last node removed will be the first in the stack
|
||||
// TODO: Instead of *just* removing it, we can use the replace mutation
|
||||
let first_element = nodes[0].find_first_element(self);
|
||||
to.insert_nodes_before(first_element, m);
|
||||
|
||||
debug_assert!(
|
||||
!nodes.is_empty(),
|
||||
"replace_nodes must have at least one node"
|
||||
);
|
||||
|
||||
self.remove_nodes(to, nodes);
|
||||
}
|
||||
|
||||
/// Remove these nodes from the dom
|
||||
/// Wont generate mutations for the inner nodes
|
||||
fn remove_nodes(&mut self, to: &mut impl WriteMutations, nodes: &[VNode]) {
|
||||
nodes
|
||||
.iter()
|
||||
.rev()
|
||||
.for_each(|node| node.remove_node(self, to, true));
|
||||
}
|
||||
|
||||
pub(crate) fn remove_component_node(
|
||||
&mut self,
|
||||
to: &mut impl WriteMutations,
|
||||
scope: ScopeId,
|
||||
gen_muts: bool,
|
||||
) {
|
||||
// Remove the component from the dom
|
||||
if let Some(mut node) = self.scopes[scope.0].last_rendered_node.take() {
|
||||
node.remove_node(self, to, gen_muts)
|
||||
};
|
||||
|
||||
// Now drop all the resources
|
||||
self.drop_scope(scope);
|
||||
}
|
||||
|
||||
/// Insert a new template into the VirtualDom's template registry
|
||||
// used in conditional compilation
|
||||
#[allow(unused_mut)]
|
||||
pub(crate) fn register_template(
|
||||
&mut self,
|
||||
to: &mut impl WriteMutations,
|
||||
mut template: Template,
|
||||
) {
|
||||
let (path, byte_index) = template.name.rsplit_once(':').unwrap();
|
||||
|
||||
let byte_index = byte_index.parse::<usize>().unwrap();
|
||||
// First, check if we've already seen this template
|
||||
if self
|
||||
.templates
|
||||
.get(&path)
|
||||
.filter(|set| set.contains_key(&byte_index))
|
||||
.is_none()
|
||||
{
|
||||
// if hot reloading is enabled, then we need to check for a template that has overriten this one
|
||||
#[cfg(debug_assertions)]
|
||||
if let Some(mut new_template) = self
|
||||
.templates
|
||||
.get_mut(path)
|
||||
.and_then(|map| map.remove(&usize::MAX))
|
||||
{
|
||||
// the byte index of the hot reloaded template could be different
|
||||
new_template.name = template.name;
|
||||
template = new_template;
|
||||
}
|
||||
|
||||
self.templates
|
||||
.entry(path)
|
||||
.or_default()
|
||||
.insert(byte_index, template);
|
||||
|
||||
// If it's all dynamic nodes, then we don't need to register it
|
||||
if !template.is_completely_dynamic() {
|
||||
to.register_template(template)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Insert a new template into the VirtualDom's template registry
|
||||
pub(crate) fn register_template_first_byte_index(&mut self, mut template: Template) {
|
||||
// First, make sure we mark the template as seen, regardless if we process it
|
||||
let (path, _) = template.name.rsplit_once(':').unwrap();
|
||||
if let Some((_, old_template)) = self
|
||||
.templates
|
||||
.entry(path)
|
||||
.or_default()
|
||||
.iter_mut()
|
||||
.min_by_key(|(byte_index, _)| **byte_index)
|
||||
{
|
||||
// the byte index of the hot reloaded template could be different
|
||||
template.name = old_template.name;
|
||||
*old_template = template;
|
||||
} else {
|
||||
// This is a template without any current instances
|
||||
self.templates
|
||||
.entry(path)
|
||||
.or_default()
|
||||
.insert(usize::MAX, template);
|
||||
}
|
||||
|
||||
// If it's all dynamic nodes, then we don't need to register it
|
||||
if !template.is_completely_dynamic() {
|
||||
self.queued_templates.push(template);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// We can apply various optimizations to dynamic nodes that are the single child of their parent.
|
||||
///
|
||||
/// IE
|
||||
/// - for text - we can use SetTextContent
|
||||
/// - for clearing children we can use RemoveChildren
|
||||
/// - for appending children we can use AppendChildren
|
||||
#[allow(dead_code)]
|
||||
fn is_dyn_node_only_child(node: &VNode, idx: usize) -> bool {
|
||||
let template = node.template.get();
|
||||
let path = template.node_paths[idx];
|
||||
|
||||
// use a loop to index every static node's children until the path has run out
|
||||
// only break if the last path index is a dynamic node
|
||||
let mut static_node = &template.roots[path[0] as usize];
|
||||
|
||||
for i in 1..path.len() - 1 {
|
||||
match static_node {
|
||||
TemplateNode::Element { children, .. } => static_node = &children[path[i] as usize],
|
||||
_ => return false,
|
||||
}
|
||||
}
|
||||
|
||||
match static_node {
|
||||
TemplateNode::Element { children, .. } => children.len() == 1,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user