mod.rs 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. #![allow(clippy::too_many_arguments)]
  2. use crate::{
  3. arena::ElementId,
  4. innerlude::{ElementRef, MountId, WriteMutations},
  5. nodes::VNode,
  6. scopes::ScopeId,
  7. virtual_dom::VirtualDom,
  8. Template, TemplateNode,
  9. };
  10. mod component;
  11. mod iterator;
  12. mod node;
  13. impl VirtualDom {
  14. pub(crate) fn create_children<'a>(
  15. &mut self,
  16. to: &mut impl WriteMutations,
  17. nodes: impl IntoIterator<Item = &'a VNode>,
  18. parent: Option<ElementRef>,
  19. ) -> usize {
  20. nodes
  21. .into_iter()
  22. .map(|child| child.create(self, to, parent.clone()))
  23. .sum()
  24. }
  25. fn remove_element_id(&mut self, to: &mut impl WriteMutations, id: ElementId, gen_muts: bool) {
  26. if gen_muts {
  27. to.remove_node(id);
  28. }
  29. self.reclaim(id)
  30. }
  31. /// Simply replace a placeholder with a list of nodes
  32. fn replace_placeholder<'a>(
  33. &mut self,
  34. to: &mut impl WriteMutations,
  35. placeholder_id: ElementId,
  36. r: impl IntoIterator<Item = &'a VNode>,
  37. parent: Option<ElementRef>,
  38. ) {
  39. let m = self.create_children(to, r, parent);
  40. to.replace_node_with(placeholder_id, m);
  41. self.reclaim(placeholder_id);
  42. }
  43. fn nodes_to_placeholder(
  44. &mut self,
  45. to: &mut impl WriteMutations,
  46. mount: MountId,
  47. dyn_node_idx: usize,
  48. old_nodes: &[VNode],
  49. ) {
  50. // Create the placeholder first, ensuring we get a dedicated ID for the placeholder
  51. let placeholder = self.next_element();
  52. // Set the id of the placeholder
  53. self.mounts[mount.0].mounted_dynamic_nodes[dyn_node_idx] = placeholder.0;
  54. to.create_placeholder(placeholder);
  55. self.replace_nodes(to, old_nodes, 1);
  56. }
  57. /// Replace many nodes with a number of nodes on the stack
  58. fn replace_nodes(&mut self, to: &mut impl WriteMutations, nodes: &[VNode], m: usize) {
  59. // We want to optimize the replace case to use one less mutation if possible
  60. // Since mutations are done in reverse, the last node removed will be the first in the stack
  61. // TODO: Instead of *just* removing it, we can use the replace mutation
  62. let first_element = nodes[0].find_first_element(self);
  63. to.insert_nodes_before(first_element, m);
  64. debug_assert!(
  65. !nodes.is_empty(),
  66. "replace_nodes must have at least one node"
  67. );
  68. self.remove_nodes(to, nodes);
  69. }
  70. /// Remove these nodes from the dom
  71. /// Wont generate mutations for the inner nodes
  72. fn remove_nodes(&mut self, to: &mut impl WriteMutations, nodes: &[VNode]) {
  73. nodes
  74. .iter()
  75. .rev()
  76. .for_each(|node| node.remove_node(self, to, true));
  77. }
  78. pub(crate) fn remove_component_node(
  79. &mut self,
  80. to: &mut impl WriteMutations,
  81. scope: ScopeId,
  82. gen_muts: bool,
  83. ) {
  84. // Remove the component from the dom
  85. if let Some(node) = self.scopes[scope.0].last_rendered_node.take() {
  86. node.remove_node(self, to, gen_muts)
  87. };
  88. // Now drop all the resources
  89. self.drop_scope(scope);
  90. }
  91. /// Insert a new template into the VirtualDom's template registry
  92. // used in conditional compilation
  93. #[allow(unused_mut)]
  94. pub(crate) fn register_template(
  95. &mut self,
  96. to: &mut impl WriteMutations,
  97. mut template: Template,
  98. ) {
  99. let (path, byte_index) = template.name.rsplit_once(':').unwrap();
  100. let byte_index = byte_index.parse::<usize>().unwrap();
  101. // First, check if we've already seen this template
  102. if self
  103. .templates
  104. .get(&path)
  105. .filter(|set| set.contains_key(&byte_index))
  106. .is_none()
  107. {
  108. // if hot reloading is enabled, then we need to check for a template that has overriten this one
  109. #[cfg(debug_assertions)]
  110. if let Some(mut new_template) = self
  111. .templates
  112. .get_mut(path)
  113. .and_then(|map| map.remove(&usize::MAX))
  114. {
  115. // the byte index of the hot reloaded template could be different
  116. new_template.name = template.name;
  117. template = new_template;
  118. }
  119. self.templates
  120. .entry(path)
  121. .or_default()
  122. .insert(byte_index, template);
  123. // If it's all dynamic nodes, then we don't need to register it
  124. if !template.is_completely_dynamic() {
  125. to.register_template(template)
  126. }
  127. }
  128. }
  129. /// Insert a new template into the VirtualDom's template registry
  130. pub(crate) fn register_template_first_byte_index(&mut self, mut template: Template) {
  131. // First, make sure we mark the template as seen, regardless if we process it
  132. let (path, _) = template.name.rsplit_once(':').unwrap();
  133. if let Some((_, old_template)) = self
  134. .templates
  135. .entry(path)
  136. .or_default()
  137. .iter_mut()
  138. .min_by_key(|(byte_index, _)| **byte_index)
  139. {
  140. // the byte index of the hot reloaded template could be different
  141. template.name = old_template.name;
  142. *old_template = template;
  143. } else {
  144. // This is a template without any current instances
  145. self.templates
  146. .entry(path)
  147. .or_default()
  148. .insert(usize::MAX, template);
  149. }
  150. // If it's all dynamic nodes, then we don't need to register it
  151. if !template.is_completely_dynamic() {
  152. self.queued_templates.push(template);
  153. }
  154. }
  155. }
  156. /// We can apply various optimizations to dynamic nodes that are the single child of their parent.
  157. ///
  158. /// IE
  159. /// - for text - we can use SetTextContent
  160. /// - for clearing children we can use RemoveChildren
  161. /// - for appending children we can use AppendChildren
  162. #[allow(dead_code)]
  163. fn is_dyn_node_only_child(node: &VNode, idx: usize) -> bool {
  164. let template = node.template.get();
  165. let path = template.node_paths[idx];
  166. // use a loop to index every static node's children until the path has run out
  167. // only break if the last path index is a dynamic node
  168. let mut static_node = &template.roots[path[0] as usize];
  169. for i in 1..path.len() - 1 {
  170. match static_node {
  171. TemplateNode::Element { children, .. } => static_node = &children[path[i] as usize],
  172. _ => return false,
  173. }
  174. }
  175. match static_node {
  176. TemplateNode::Element { children, .. } => children.len() == 1,
  177. _ => false,
  178. }
  179. }