rehydrate.rs 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. use crate::dom::WebsysDom;
  2. use dioxus_core::{
  3. AttributeValue, DynamicNode, ElementId, ScopeState, TemplateNode, VNode, VPlaceholder, VText,
  4. VirtualDom,
  5. };
  6. use dioxus_html::event_bubbles;
  7. use wasm_bindgen::JsCast;
  8. use web_sys::{Comment, Node};
  9. #[derive(Debug, Copy, Clone)]
  10. pub enum RehydrationError {
  11. NodeTypeMismatch,
  12. NodeNotFound,
  13. VNodeNotInitialized,
  14. }
  15. use RehydrationError::*;
  16. fn set_node(hydrated: &mut Vec<bool>, id: ElementId, node: Node) {
  17. let idx = id.0;
  18. if idx >= hydrated.len() {
  19. hydrated.resize(idx + 1, false);
  20. }
  21. if !hydrated[idx] {
  22. dioxus_interpreter_js::set_node(idx as u32, node);
  23. hydrated[idx] = true;
  24. }
  25. }
  26. impl WebsysDom {
  27. // we're streaming in patches, but the nodes already exist
  28. // so we're just going to write the correct IDs to the node and load them in
  29. pub fn rehydrate(&mut self, dom: &VirtualDom) -> Result<(), RehydrationError> {
  30. let mut root = self
  31. .root
  32. .clone()
  33. .dyn_into::<Node>()
  34. .map_err(|_| NodeTypeMismatch)?
  35. .first_child()
  36. .ok_or(NodeNotFound);
  37. let root_scope = dom.base_scope();
  38. let mut hydrated = vec![true];
  39. let mut last_node_was_static_text = false;
  40. // Recursively rehydrate the dom from the VirtualDom
  41. self.rehydrate_scope(
  42. root_scope,
  43. &mut root,
  44. &mut hydrated,
  45. dom,
  46. &mut last_node_was_static_text,
  47. )?;
  48. self.interpreter.flush();
  49. Ok(())
  50. }
  51. fn rehydrate_scope(
  52. &mut self,
  53. scope: &ScopeState,
  54. current_child: &mut Result<Node, RehydrationError>,
  55. hydrated: &mut Vec<bool>,
  56. dom: &VirtualDom,
  57. last_node_was_static_text: &mut bool,
  58. ) -> Result<(), RehydrationError> {
  59. let vnode = match scope.root_node() {
  60. dioxus_core::RenderReturn::Ready(ready) => ready,
  61. _ => return Err(VNodeNotInitialized),
  62. };
  63. self.rehydrate_vnode(
  64. current_child,
  65. hydrated,
  66. dom,
  67. vnode,
  68. last_node_was_static_text,
  69. )
  70. }
  71. fn rehydrate_vnode(
  72. &mut self,
  73. current_child: &mut Result<Node, RehydrationError>,
  74. hydrated: &mut Vec<bool>,
  75. dom: &VirtualDom,
  76. vnode: &VNode,
  77. last_node_was_static_text: &mut bool,
  78. ) -> Result<(), RehydrationError> {
  79. for (i, root) in vnode.template.get().roots.iter().enumerate() {
  80. // make sure we set the root node ids even if the node is not dynamic
  81. set_node(
  82. hydrated,
  83. *vnode.root_ids.borrow().get(i).ok_or(VNodeNotInitialized)?,
  84. current_child.clone()?,
  85. );
  86. self.rehydrate_template_node(
  87. current_child,
  88. hydrated,
  89. dom,
  90. vnode,
  91. root,
  92. last_node_was_static_text,
  93. )?;
  94. }
  95. Ok(())
  96. }
  97. fn rehydrate_template_node(
  98. &mut self,
  99. current_child: &mut Result<Node, RehydrationError>,
  100. hydrated: &mut Vec<bool>,
  101. dom: &VirtualDom,
  102. vnode: &VNode,
  103. node: &TemplateNode,
  104. last_node_was_static_text: &mut bool,
  105. ) -> Result<(), RehydrationError> {
  106. log::trace!("rehydrate template node: {:?}", node);
  107. if let Ok(current_child) = current_child {
  108. if log::log_enabled!(log::Level::Trace) {
  109. web_sys::console::log_1(&current_child.clone().into());
  110. }
  111. }
  112. match node {
  113. TemplateNode::Element {
  114. children, attrs, ..
  115. } => {
  116. let mut mounted_id = None;
  117. for attr in *attrs {
  118. if let dioxus_core::TemplateAttribute::Dynamic { id } = attr {
  119. let attribute = &vnode.dynamic_attrs[*id];
  120. let value = &attribute.value;
  121. let id = attribute.mounted_element.get();
  122. mounted_id = Some(id);
  123. let name = attribute.name;
  124. if let AttributeValue::Listener(_) = value {
  125. let event_name = &name[2..];
  126. self.interpreter.new_event_listener(
  127. event_name,
  128. id.0 as u32,
  129. event_bubbles(event_name) as u8,
  130. );
  131. }
  132. }
  133. }
  134. if let Some(id) = mounted_id {
  135. set_node(hydrated, id, current_child.clone()?);
  136. }
  137. if !children.is_empty() {
  138. let mut children_current_child = current_child
  139. .as_mut()
  140. .map_err(|e| *e)?
  141. .first_child()
  142. .ok_or(NodeNotFound)?
  143. .dyn_into::<Node>()
  144. .map_err(|_| NodeTypeMismatch);
  145. for child in *children {
  146. self.rehydrate_template_node(
  147. &mut children_current_child,
  148. hydrated,
  149. dom,
  150. vnode,
  151. child,
  152. last_node_was_static_text,
  153. )?;
  154. }
  155. }
  156. *current_child = current_child
  157. .as_mut()
  158. .map_err(|e| *e)?
  159. .next_sibling()
  160. .ok_or(NodeNotFound);
  161. *last_node_was_static_text = false;
  162. }
  163. TemplateNode::Text { .. } => {
  164. // if the last node was static text, it got merged with this one
  165. if !*last_node_was_static_text {
  166. *current_child = current_child
  167. .as_mut()
  168. .map_err(|e| *e)?
  169. .next_sibling()
  170. .ok_or(NodeNotFound);
  171. }
  172. *last_node_was_static_text = true;
  173. }
  174. TemplateNode::Dynamic { id } | TemplateNode::DynamicText { id } => {
  175. self.rehydrate_dynamic_node(
  176. current_child,
  177. hydrated,
  178. dom,
  179. &vnode.dynamic_nodes[*id],
  180. last_node_was_static_text,
  181. )?;
  182. }
  183. }
  184. Ok(())
  185. }
  186. fn rehydrate_dynamic_node(
  187. &mut self,
  188. current_child: &mut Result<Node, RehydrationError>,
  189. hydrated: &mut Vec<bool>,
  190. dom: &VirtualDom,
  191. dynamic: &DynamicNode,
  192. last_node_was_static_text: &mut bool,
  193. ) -> Result<(), RehydrationError> {
  194. log::trace!("rehydrate dynamic node: {:?}", dynamic);
  195. if let Ok(current_child) = current_child {
  196. if log::log_enabled!(log::Level::Trace) {
  197. web_sys::console::log_1(&current_child.clone().into());
  198. }
  199. }
  200. match dynamic {
  201. dioxus_core::DynamicNode::Text(VText { id, .. }) => {
  202. // skip comment separator before node
  203. if cfg!(debug_assertions) {
  204. assert!(current_child
  205. .as_mut()
  206. .map_err(|e| *e)?
  207. .has_type::<Comment>());
  208. }
  209. *current_child = current_child
  210. .as_mut()
  211. .map_err(|e| *e)?
  212. .next_sibling()
  213. .ok_or(NodeNotFound);
  214. set_node(
  215. hydrated,
  216. id.get().ok_or(VNodeNotInitialized)?,
  217. current_child.clone()?,
  218. );
  219. *current_child = current_child
  220. .as_mut()
  221. .map_err(|e| *e)?
  222. .next_sibling()
  223. .ok_or(NodeNotFound);
  224. // skip comment separator after node
  225. if cfg!(debug_assertions) {
  226. assert!(current_child
  227. .as_mut()
  228. .map_err(|e| *e)?
  229. .has_type::<Comment>());
  230. }
  231. *current_child = current_child
  232. .as_mut()
  233. .map_err(|e| *e)?
  234. .next_sibling()
  235. .ok_or(NodeNotFound);
  236. *last_node_was_static_text = false;
  237. }
  238. dioxus_core::DynamicNode::Placeholder(VPlaceholder { id, .. }) => {
  239. set_node(
  240. hydrated,
  241. id.get().ok_or(VNodeNotInitialized)?,
  242. current_child.clone()?,
  243. );
  244. *current_child = current_child
  245. .as_mut()
  246. .map_err(|e| *e)?
  247. .next_sibling()
  248. .ok_or(NodeNotFound);
  249. *last_node_was_static_text = false;
  250. }
  251. dioxus_core::DynamicNode::Component(comp) => {
  252. let scope = comp.scope.get().ok_or(VNodeNotInitialized)?;
  253. self.rehydrate_scope(
  254. dom.get_scope(scope).unwrap(),
  255. current_child,
  256. hydrated,
  257. dom,
  258. last_node_was_static_text,
  259. )?;
  260. }
  261. dioxus_core::DynamicNode::Fragment(fragment) => {
  262. for vnode in *fragment {
  263. self.rehydrate_vnode(
  264. current_child,
  265. hydrated,
  266. dom,
  267. vnode,
  268. last_node_was_static_text,
  269. )?;
  270. }
  271. }
  272. }
  273. Ok(())
  274. }
  275. }