瀏覽代碼

chore: add some fixes to hot reload

Jonathan Kelley 2 年之前
父節點
當前提交
366e46eddf
共有 4 個文件被更改,包括 34 次插入19 次删除
  1. 12 3
      packages/core/src/diff.rs
  2. 8 1
      packages/router/src/components/link.rs
  3. 4 6
      packages/web/src/dom.rs
  4. 10 9
      packages/web/src/hot_reload.rs

+ 12 - 3
packages/core/src/diff.rs

@@ -840,8 +840,11 @@ impl<'b> VirtualDom {
                 .map(|path| path.len());
             // if the path is 1 the attribute is in the root, so we don't need to clean it up
             // if the path is 0, the attribute is a not attached at all, so we don't need to clean it up
-            if let Some(..=1) = path_len {
-                continue;
+
+            if let Some(len) = path_len {
+                if (..=1).contains(&len) {
+                    continue;
+                }
             }
 
             let next_id = attr.mounted_element.get();
@@ -908,7 +911,13 @@ impl<'b> VirtualDom {
 
         match unsafe { self.scopes[scope.0].root_node().extend_lifetime_ref() } {
             RenderReturn::Ready(t) => self.remove_node(t, gen_muts),
-            _ => todo!("cannot handle nonstandard nodes"),
+            RenderReturn::Aborted(t) => {
+                if let Some(id) = t.id.get() {
+                    self.try_reclaim(id);
+                }
+                return;
+            }
+            _ => todo!(),
         };
 
         let props = self.scopes[scope.0].props.take();

+ 8 - 1
packages/router/src/components/link.rs

@@ -55,6 +55,9 @@ pub struct LinkProps<'a> {
 
     /// Pass children into the `<a>` element
     pub children: Element<'a>,
+
+    /// The onclick event handler.
+    pub onclick: Option<EventHandler<'a, MouseEvent>>,
 }
 
 /// A component that renders a link to a route.
@@ -119,7 +122,7 @@ pub fn Link<'a>(cx: Scope<'a, LinkProps<'a>>) -> Element {
             title: format_args!("{}", title.unwrap_or("")),
             prevent_default: "{prevent_default}",
             target: format_args!("{}", if * new_tab { "_blank" } else { "" }),
-            onclick: move |_| {
+            onclick: move |evt| {
                 log::trace!("Clicked link to {}", to);
 
                 if !outerlink {
@@ -138,6 +141,10 @@ pub fn Link<'a>(cx: Scope<'a, LinkProps<'a>>) -> Element {
                         );
                     }
                 }
+
+                if let Some(onclick) = cx.props.onclick.as_ref() {
+                    onclick.call(evt);
+                }
             },
             children
         }

+ 4 - 6
packages/web/src/dom.rs

@@ -121,12 +121,10 @@ impl WebsysDom {
                     } = attr
                     {
                         match namespace {
-                            Some(ns) if *ns == "style" => el
-                                .dyn_ref::<HtmlElement>()
-                                .unwrap()
-                                .style()
-                                .set_property(name, value)
-                                .unwrap(),
+                            Some(ns) if *ns == "style" => {
+                                el.dyn_ref::<HtmlElement>()
+                                    .map(|f| f.style().set_property(name, value));
+                            }
                             Some(ns) => el.set_attribute_ns(Some(ns), name, value).unwrap(),
                             None => el.set_attribute(name, value).unwrap(),
                         }

+ 10 - 9
packages/web/src/hot_reload.rs

@@ -18,15 +18,15 @@ pub(crate) fn init() -> UnboundedReceiver<Template<'static>> {
 
 #[cfg(debug_assertions)]
 pub(crate) fn init() -> UnboundedReceiver<Template<'static>> {
-    use core::panic;
     use std::convert::TryInto;
 
+    use serde::Deserialize;
+
     let window = web_sys::window().unwrap();
 
-    let protocol = if window.location().protocol().unwrap() == "https:" {
-        "wss:"
-    } else {
-        "ws:"
+    let protocol = match window.location().protocol().unwrap() {
+        prot if prot == "https:" => "wss:",
+        _ => "ws:",
     };
 
     let url = format!(
@@ -43,10 +43,11 @@ pub(crate) fn init() -> UnboundedReceiver<Template<'static>> {
         if let Ok(text) = e.data().dyn_into::<js_sys::JsString>() {
             let text: Result<String, _> = text.try_into();
             if let Ok(string) = text {
-                match serde_json::from_str(Box::leak(string.into_boxed_str())) {
-                    Ok(template) => _ = tx.unbounded_send(template),
-                    Err(e) => panic!("Failed to parse template: {}", e),
-                }
+                let val = serde_json::from_str::<serde_json::Value>(&string).unwrap();
+                // leak the value
+                let val: &'static serde_json::Value = Box::leak(Box::new(val));
+                let template: Template<'_> = Template::deserialize(val).unwrap();
+                tx.unbounded_send(template).unwrap();
             }
         }
     }) as Box<dyn FnMut(MessageEvent)>);