rehydrate.rs 10 KB

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