mod.rs 6.7 KB

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