ソースを参照

wip: it works but the page is backwards

Jonathan Kelley 3 年 前
コミット
cdcd8611e8

+ 1 - 16
packages/core/benches/create.rs

@@ -1,16 +1 @@
-use criterion::{black_box, criterion_group, criterion_main, Criterion};
-
-fn fibonacci(n: u64) -> u64 {
-    match n {
-        0 => 1,
-        1 => 1,
-        n => fibonacci(n - 1) + fibonacci(n - 2),
-    }
-}
-
-fn criterion_benchmark(c: &mut Criterion) {
-    c.bench_function("fib 20", |b| b.iter(|| fibonacci(black_box(20))));
-}
-
-criterion_group!(benches, criterion_benchmark);
-criterion_main!(benches);
+fn main() {}

+ 5 - 10
packages/core/benches/jsframework.rs

@@ -1,3 +1,4 @@
+#![allow(non_snake_case, non_upper_case_globals)]
 //! This benchmark tests just the overhead of Dioxus itself.
 //!
 //! For the JS Framework Benchmark, both the framework and the browser is benchmarked together. Dioxus prepares changes
@@ -8,12 +9,10 @@
 //! - Dioxus takes 3ms to create 1_000 rows
 //! - Dioxus takes 30ms to create 10_000 rows
 //!
-//! As pure "overhead", these are really really good numbers, mostly slowed down by hitting the global allocator.
+//! As pure "overhead", these are amazing good numbers, mostly slowed down by hitting the global allocator.
 //! These numbers don't represent Dioxus with the heuristic engine installed, so I assume it'll be even faster.
 
-use std::fmt::Display;
-
-use criterion::{black_box, criterion_group, criterion_main, Criterion};
+use criterion::{criterion_group, criterion_main, Criterion};
 use dioxus_core as dioxus;
 use dioxus_core::prelude::*;
 use dioxus_html as dioxus_elements;
@@ -58,11 +57,12 @@ struct RowProps {
     label: Label,
 }
 fn Row<'a>(cx: Context<'a, RowProps>) -> DomTree {
+    let [adj, col, noun] = cx.label.0;
     cx.render(rsx! {
         tr {
             td { class:"col-md-1", "{cx.row_id}" }
             td { class:"col-md-1", onclick: move |_| { /* run onselect */ }
-                a { class: "lbl", "{cx.label}" }
+                a { class: "lbl", "{adj}" "{col}" "{noun}" }
             }
             td { class: "col-md-1"
                 a { class: "remove", onclick: move |_| {/* remove */}
@@ -86,11 +86,6 @@ impl Label {
         ])
     }
 }
-impl Display for Label {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        write!(f, "{} {} {}", self.0[0], self.0[1], self.0[2])
-    }
-}
 
 static ADJECTIVES: &[&str] = &[
     "pretty",

+ 4 - 5
packages/core/examples/alternative.rs

@@ -6,17 +6,16 @@ pub static Example: FC<()> = |cx| {
     let list = (0..10).map(|f| LazyNodes::new(move |f| todo!()));
 
     cx.render(LazyNodes::new(move |cx| {
-        let bump = cx.bump();
         cx.raw_element(
             "div",
             None,
-            &mut [],
-            &mut [],
-            cx.bump().alloc([
+            [],
+            [],
+            [
                 cx.text(format_args!("hello")),
                 cx.text(format_args!("hello")),
                 cx.fragment_from_iter(list),
-            ]),
+            ],
             None,
         )
     }))

+ 0 - 18
packages/core/examples/fragment.rs

@@ -1,18 +0,0 @@
-
-
-fn main() {
-
-    // let g = rsx! {
-    //     Fragment {
-    //         // div {}
-    //         // div {}
-    //         // div {}
-    //         // div {}
-    //         // div {}
-    //         // div {}
-    //         // div {}
-    //         // div {}
-    //         // div {}
-    //     }
-    // };
-}

+ 13 - 17
packages/core/examples/fragment_from_iter.rs

@@ -1,31 +1,27 @@
-fn main() {}
-
 use dioxus_core::prelude::*;
 
-fn App(cx: Context<()>) -> DomTree {
-    //
+fn main() {}
+
+fn app(cx: Context<()>) -> DomTree {
     let vak = use_suspense(
         cx,
         || async {},
-        |c, res| {
-            //
-            c.render(LazyNodes::new(move |f| f.text(format_args!(""))))
-        },
+        |c, _res| c.render(LazyNodes::new(move |f| f.text(format_args!("")))),
     );
 
     let d1 = cx.render(LazyNodes::new(move |f| {
         f.raw_element(
             "div",
             None,
-            &mut [],
-            &[],
-            f.bump().alloc([
+            [],
+            [],
+            [
                 f.fragment_from_iter(vak),
                 f.text(format_args!("")),
                 f.text(format_args!("")),
                 f.text(format_args!("")),
                 f.text(format_args!("")),
-            ]),
+            ],
             None,
         )
     }));
@@ -34,15 +30,15 @@ fn App(cx: Context<()>) -> DomTree {
         f.raw_element(
             "div",
             None,
-            &mut [],
-            &[],
-            f.bump().alloc([
+            [],
+            [],
+            [
                 f.text(format_args!("")),
                 f.text(format_args!("")),
                 f.text(format_args!("")),
                 f.text(format_args!("")),
-                f.fragment_from_iter(d1),
-            ]),
+                d1.unwrap(),
+            ],
             None,
         )
     }))

+ 0 - 137
packages/core/examples/macrosrc.rs

@@ -1,137 +0,0 @@
-// #![allow(unused, non_upper_case_globals, non_snake_case)]
-// use bumpalo::Bump;
-// use dioxus_core::nodebuilder::*;
-// use dioxus_core::prelude::*;
-// use std::{collections::HashMap, future::Future, marker::PhantomData};
-
-fn main() {}
-// fn main() {
-//     let mut vdom = VirtualDom::new_with_props(
-//         component,
-//         Props {
-//             blah: false,
-//             text: "blah".into(),
-//         },
-//     );
-
-//     vdom.progress();
-
-//     let somet = String::from("asd");
-//     let text = somet.as_str();
-
-//     /*
-//     this could be auto-generated via the macro
-//     this props is allocated in this
-//     but the component and props would like need to be cached
-//     we could box this fn, abstracting away the props requirement and just keep the entrance and allocator requirement
-//     How do we keep cached things around?
-//     Need some sort of caching mechanism
-
-//     how do we enter into a childscope from a parent scope?
-
-//     Problems:
-//     1: Comp props need to be stored somewhere so we can re-evalute components when they receive updates
-//     2: Trees are not evaluated
-
-//     */
-//     let example_caller = move |cx: &Bump| {
-//         todo!()
-//         // let p = Props { blah: true, text };
-//         // let c = Context { props: &p };
-//         // let r = component(&c);
-//     };
-
-//     // check the edit list
-// }
-
-// // ~~~ Text shared between components via props can be done with lifetimes! ~~~
-// // Super duper efficient :)
-// struct Props {
-//     blah: bool,
-//     text: String,
-//     // text: &'src str,
-// }
-// impl Properties for Props {
-//     fn new() -> Self {
-//         todo!()
-//     }
-// }
-
-// fn component<'a>(cx: Context<'a, Props>) -> DomTree<'a> {
-//     // Write asynchronous rendering code that immediately returns a "suspended" VNode
-//     // The concurrent API will then progress this component when the future finishes
-//     // You can suspend the entire component, or just parts of it
-//     let product_list = cx.suspend(async {
-//         // Suspend the rendering that completes when the future is done
-//         match fetch_data().await {
-//             Ok(data) => html! { <div> "success!" </div>},
-//             Err(_) => html! { <div> "failure :(" </div>},
-//         }
-//     });
-
-//     // todo!()
-//     cx.render(html! {
-//         <div>
-//             <h1> "Products" </h1>
-//             <button> "hello!" </button>
-//             // Subnodes can even be suspended
-//             // When completely rendered, they won't cause the component itself to re-render, just their slot
-
-//             // <p> { product_list } </p>
-//         </div>
-//     })
-// }
-
-// fn BuilderComp<'a>(cx: Context<'a, Props>) -> DomTree<'a> {
-//     // VNodes can be constructed via a builder or the html! macro
-//     // However, both of these are "lazy" - they need to be evaluated (aka, "viewed")
-//     // We can "view" them with Context for ultimate speed while inside components
-//     cx.render(|bump| {
-//         div(bump)
-//             .attr("class", "edit")
-//             .child(text("Hello"))
-//             .child(text(cx.cx.text.as_ref()))
-//             .finish()
-//     })
-// }
-
-// #[fc]
-// fn EffcComp(cx: Context, name: &str) -> DomTree {
-//     // VNodes can be constructed via a builder or the html! macro
-//     // However, both of these are "lazy" - they need to be evaluated (aka, "viewed")
-//     // We can "view" them with Context for ultimate speed while inside components
-//     // use "phase" style allocation;
-
-//     cx.render(html! {
-//         <div>
-//             // your template goes here
-//             // feel free to directly use "name"
-//         </div>
-//     })
-// }
-
-// fn FullySuspended<'a>(cx: &'a Context<Props>) -> DomTree<'a> {
-//     cx.suspend(async {
-//         let i: i32 = 0;
-
-//         // full suspended works great with just returning VNodes!
-//         // Feel free to capture the html! macro directly
-//         // Anything returned here is automatically viewed
-//         let tex = match i {
-//             1 => html! { <div> </div> },
-//             2 => html! { <div> </div> },
-//             _ => html! { <div> </div> },
-//         };
-
-//         if cx.cx.blah {
-//             html! { <div> </div> }
-//         } else {
-//             tex
-//         }
-//     })
-// }
-
-// /// An example of a datafetching service
-// async fn fetch_data() -> Result<String, ()> {
-//     todo!()
-// }

+ 19 - 21
packages/core/src/diff.rs

@@ -89,7 +89,6 @@
 //!  - https://hacks.mozilla.org/2019/03/fast-bump-allocated-virtual-doms-with-rust-and-wasm/
 
 use crate::{arena::SharedResources, innerlude::*};
-use futures_util::{Future, FutureExt};
 use fxhash::{FxHashMap, FxHashSet};
 use DomEdit::*;
 
@@ -281,19 +280,20 @@ impl<'bump> DiffMachine<'bump> {
         } = element;
 
         let real_id = self.vdom.reserve_node();
+        dom_id.set(Some(real_id));
+
         self.mutations.create_element(tag_name, *namespace, real_id);
 
         self.stack.add_child_count(1);
 
-        dom_id.set(Some(real_id));
-
-        let cur_scope = self.stack.current_scope().unwrap();
+        let cur_scope_id = self.stack.current_scope().unwrap();
+        let scope = self.vdom.get_scope(cur_scope_id).unwrap();
 
         listeners.iter().for_each(|listener| {
-            self.fix_listener(listener);
+            self.attach_listener_to_scope(listener, scope);
             listener.mounted_node.set(Some(real_id));
             self.mutations
-                .new_event_listener(listener, cur_scope.clone());
+                .new_event_listener(listener, cur_scope_id.clone());
         });
 
         for attr in *attributes {
@@ -466,16 +466,19 @@ impl<'bump> DiffMachine<'bump> {
         // We also need to make sure that all listeners are properly attached to the parent scope (fix_listener)
         //
         // TODO: take a more efficient path than this
-        let cur_scope = self.stack.current_scope().unwrap();
+
+        let cur_scope_id = self.stack.current_scope().unwrap();
+        let scope = self.vdom.get_scope(cur_scope_id).unwrap();
+
         if old.listeners.len() == new.listeners.len() {
             for (old_l, new_l) in old.listeners.iter().zip(new.listeners.iter()) {
                 if old_l.event != new_l.event {
                     please_commit(&mut self.mutations.edits);
                     self.mutations.remove_event_listener(old_l.event);
-                    self.mutations.new_event_listener(new_l, cur_scope);
+                    self.mutations.new_event_listener(new_l, cur_scope_id);
                 }
                 new_l.mounted_node.set(old_l.mounted_node.get());
-                self.fix_listener(new_l);
+                self.attach_listener_to_scope(new_l, scope);
             }
         } else {
             please_commit(&mut self.mutations.edits);
@@ -484,10 +487,8 @@ impl<'bump> DiffMachine<'bump> {
             }
             for listener in new.listeners {
                 listener.mounted_node.set(Some(root));
-                self.mutations.new_event_listener(listener, cur_scope);
-
-                // Make sure the listener gets attached to the scope list
-                self.fix_listener(listener);
+                self.mutations.new_event_listener(listener, cur_scope_id);
+                self.attach_listener_to_scope(listener, scope);
             }
         }
 
@@ -1082,13 +1083,10 @@ impl<'bump> DiffMachine<'bump> {
         }
     }
 
-    fn fix_listener<'a>(&mut self, listener: &'a Listener<'a>) {
-        let scope_id = self.stack.current_scope();
-        if let Some(scope_id) = scope_id {
-            let scope = self.vdom.get_scope(scope_id).unwrap();
-            let mut queue = scope.listeners.borrow_mut();
-            let long_listener: &'a Listener<'static> = unsafe { std::mem::transmute(listener) };
-            queue.push(long_listener as *const _)
-        }
+    /// Adds a listener closure to a scope during diff.
+    fn attach_listener_to_scope<'a>(&mut self, listener: &'a Listener<'a>, scope: &Scope) {
+        let mut queue = scope.listeners.borrow_mut();
+        let long_listener: &'a Listener<'static> = unsafe { std::mem::transmute(listener) };
+        queue.push(long_listener as *const _)
     }
 }

+ 2 - 3
packages/core/src/scope.rs

@@ -113,7 +113,6 @@ impl Scope {
         child_nodes: ScopeChildren,
     ) {
         self.caller = caller;
-        // let child_nodes = unsafe { std::mem::transmute(child_nodes) };
         let child_nodes = unsafe { child_nodes.extend_lifetime() };
         self.child_nodes = child_nodes;
     }
@@ -237,8 +236,8 @@ impl Scope {
     }
 
     pub fn consume_garbage(&self) -> Vec<&VNode> {
-        let mut garbage = self.pending_garbage.borrow_mut();
-        garbage
+        self.pending_garbage
+            .borrow_mut()
             .drain(..)
             .map(|node| {
                 // safety: scopes cannot cycle without their garbage being collected. these nodes are safe

+ 8 - 6
packages/core/tests/create_iterative.rs

@@ -7,6 +7,8 @@ use dioxus_core as dioxus;
 use dioxus_html as dioxus_elements;
 use DomEdit::*;
 
+const LOGGING_ENABLED: bool = false;
+
 #[test]
 fn test_original_diff() {
     static App: FC<()> = |cx| {
@@ -57,7 +59,7 @@ async fn create() {
         })
     };
 
-    test_logging::set_up_logging();
+    test_logging::set_up_logging(LOGGING_ENABLED);
     let mut dom = VirtualDom::new(App);
     let mutations = dom.rebuild_async().await.unwrap();
     assert_eq!(
@@ -98,7 +100,7 @@ async fn create_list() {
         })
     };
 
-    test_logging::set_up_logging();
+    test_logging::set_up_logging(LOGGING_ENABLED);
 
     let mut dom = VirtualDom::new(App);
     let mutations = dom.rebuild_async().await.unwrap();
@@ -141,7 +143,7 @@ async fn create_simple() {
         })
     };
 
-    test_logging::set_up_logging();
+    test_logging::set_up_logging(LOGGING_ENABLED);
 
     let mut dom = VirtualDom::new(App);
     let mutations = dom.rebuild_async().await.unwrap();
@@ -177,7 +179,7 @@ async fn create_components() {
         })
     };
 
-    test_logging::set_up_logging();
+    test_logging::set_up_logging(LOGGING_ENABLED);
 
     let mut dom = VirtualDom::new(App);
     let mutations = dom.rebuild_async().await.unwrap();
@@ -223,7 +225,7 @@ async fn anchors() {
         })
     };
 
-    test_logging::set_up_logging();
+    test_logging::set_up_logging(LOGGING_ENABLED);
 
     let mut dom = VirtualDom::new(App);
     let mutations = dom.rebuild_async().await.unwrap();
@@ -249,7 +251,7 @@ async fn suspended() {
         cx.render(rsx! { {val} })
     };
 
-    test_logging::set_up_logging();
+    test_logging::set_up_logging(LOGGING_ENABLED);
 
     let mut dom = VirtualDom::new(App);
     let mutations = dom.rebuild_async().await.unwrap();

+ 3 - 1
packages/core/tests/diff_iterative.rs

@@ -6,6 +6,8 @@ mod test_logging;
 use dioxus_core as dioxus;
 use dioxus_html as dioxus_elements;
 
+const LOGGING_ENABLED: bool = false;
+
 #[async_std::test]
 async fn test_iterative_create_components() {
     static App: FC<()> = |cx| {
@@ -34,7 +36,7 @@ async fn test_iterative_create_components() {
         })
     }
 
-    test_logging::set_up_logging();
+    test_logging::set_up_logging(LOGGING_ENABLED);
 
     let mut dom = VirtualDom::new(App);
 

+ 57 - 43
packages/core/tests/diffing.rs

@@ -1,12 +1,7 @@
 //! Diffing Tests
-//! -------------
-//!
-//! These should always compile and run, but the result is not validat root: (), m: () ed for each test.
-//! TODO: Validate the results beyond visual inspection.
 
 use bumpalo::Bump;
 
-use anyhow::{Context, Result};
 use dioxus::{
     arena::SharedResources, diff::DiffMachine, prelude::*, DiffInstruction, DomEdit, MountType,
 };
@@ -18,7 +13,7 @@ use DomEdit::*;
 
 // logging is wired up to the test harness
 // feel free to enable while debugging
-const IS_LOGGING_ENABLED: bool = true;
+const IS_LOGGING_ENABLED: bool = false;
 
 struct TestDom {
     bump: Bump,
@@ -26,9 +21,7 @@ struct TestDom {
 }
 impl TestDom {
     fn new() -> TestDom {
-        if IS_LOGGING_ENABLED {
-            test_logging::set_up_logging();
-        }
+        test_logging::set_up_logging(IS_LOGGING_ENABLED);
         let bump = Bump::new();
         let resources = SharedResources::new();
         TestDom { bump, resources }
@@ -82,8 +75,6 @@ impl TestDom {
 
         let new = self.bump.alloc(self.render(right));
 
-        // let mut create_edits = Vec::new();
-
         let mut machine = DiffMachine::new_headless(&self.resources);
 
         machine.stack.create_node(old, MountType::Append);
@@ -343,16 +334,31 @@ fn two_fragments_with_differrent_elements_are_differet() {
     let dom = TestDom::new();
 
     let left = rsx!(
-        { (0..2).map(|f| rsx! { div {  }} ) }
+        { (0..2).map(|_| rsx! { div {  }} ) }
         p {}
     );
     let right = rsx!(
-        { (0..5).map(|f| rsx! (h1 {  }) ) }
+        { (0..5).map(|_| rsx! (h1 {  }) ) }
         p {}
     );
 
-    let edits = dom.lazy_diff(left, right);
-    log::debug!("{:#?}", &edits);
+    let (create, changes) = dom.lazy_diff(left, right);
+    log::debug!("{:#?}", &changes);
+    assert_eq!(
+        changes.edits,
+        [
+            // create the new h1s
+            CreateElement { tag: "h1", id: 3 },
+            CreateElement { tag: "h1", id: 4 },
+            CreateElement { tag: "h1", id: 5 },
+            InsertAfter { root: 1, n: 3 },
+            // replace the divs with new h1s
+            CreateElement { tag: "h1", id: 6 },
+            ReplaceWith { root: 0, m: 1 },
+            CreateElement { tag: "h1", id: 7 },
+            ReplaceWith { root: 1, m: 1 },
+        ]
+    );
 }
 
 /// Should result in multiple nodes destroyed - with changes to the first nodes
@@ -452,14 +458,6 @@ fn keyed_diffing_order() {
     );
 }
 
-#[test]
-fn fragment_keys() {
-    let r = 1;
-    let p = rsx! {
-        Fragment { key: "asd {r}" }
-    };
-}
-
 /// Should result in moves, but not removals or additions
 #[test]
 fn keyed_diffing_out_of_order() {
@@ -477,8 +475,12 @@ fn keyed_diffing_out_of_order() {
         })
     });
 
-    let edits = dom.lazy_diff(left, right);
-    log::debug!("{:?}", &edits.1);
+    let (_, changes) = dom.lazy_diff(left, right);
+    log::debug!("{:?}", &changes);
+    assert_eq!(
+        changes.edits,
+        [PushRoot { id: 6 }, InsertBefore { root: 4, n: 1 }]
+    );
 }
 
 /// Should result in moves only
@@ -655,21 +657,24 @@ fn keyed_diffing_additions_and_moves_on_ends() {
 
     let right = rsx!({
         [/**/ 7, 4, 5, 6, 11, 12 /**/].iter().map(|f| {
-            // [/**/ 8, 7, 4, 5, 6, 9, 10 /**/].iter().map(|f| {
             rsx! { div { key: "{f}"  }}
         })
     });
 
     let (_, change) = dom.lazy_diff(left, right);
     log::debug!("{:?}", change);
-    // assert_eq!(
-    //     change.edits,
-    //     [
-    //         CreateElement { id: 5, tag: "div" },
-    //         CreateElement { id: 6, tag: "div" },
-    //         InsertAfter { n: 2, root: 4 }
-    //     ]
-    // );
+    assert_eq!(
+        change.edits,
+        [
+            // create 11, 12
+            CreateElement { tag: "div", id: 4 },
+            CreateElement { tag: "div", id: 5 },
+            InsertAfter { root: 2, n: 2 },
+            // move 7 to the front
+            PushRoot { id: 3 },
+            InsertBefore { root: 0, n: 1 }
+        ]
+    );
 }
 
 #[test]
@@ -688,16 +693,25 @@ fn keyed_diffing_additions_and_moves_in_middle() {
         })
     });
 
+    // LIS: 4, 5, 6
     let (_, change) = dom.lazy_diff(left, right);
-    log::debug!("{:?}", change);
-    // assert_eq!(
-    //     change.edits,
-    //     [
-    //         CreateElement { id: 5, tag: "div" },
-    //         CreateElement { id: 6, tag: "div" },
-    //         InsertAfter { n: 2, root: 4 }
-    //     ]
-    // );
+    log::debug!("{:#?}", change);
+    assert_eq!(
+        change.edits,
+        [
+            // create 13, 17
+            CreateElement { tag: "div", id: 4 },
+            CreateElement { tag: "div", id: 5 },
+            InsertBefore { root: 1, n: 2 },
+            // create 11, 12
+            CreateElement { tag: "div", id: 6 },
+            CreateElement { tag: "div", id: 7 },
+            InsertBefore { root: 2, n: 2 },
+            // move 7
+            PushRoot { id: 3 },
+            InsertBefore { root: 0, n: 1 }
+        ]
+    );
 }
 
 #[test]

+ 5 - 1
packages/core/tests/test_logging.rs

@@ -1,7 +1,11 @@
-pub fn set_up_logging() {
+pub fn set_up_logging(enabled: bool) {
     use fern::colors::{Color, ColoredLevelConfig};
     use log::debug;
 
+    if !enabled {
+        return;
+    }
+
     // configure colors for the whole line
     let colors_line = ColoredLevelConfig::new()
         .error(Color::Red)

+ 3 - 0
packages/html/src/attrval.s → packages/html/src/attrval.rs

@@ -1,3 +1,6 @@
+//! This module is not included anywhere.
+//!
+//! It is a prototype for a system that supports non-string attribute values.
 
 trait AsAttributeValue: Sized {
     fn into_attribute_value<'a>(self, cx: NodeFactory<'a>) -> AttributeValue<'a>;

+ 25 - 0
packages/ssr/examples/basic.rs

@@ -0,0 +1,25 @@
+use dioxus::virtual_dom::VirtualDom;
+use dioxus_core as dioxus;
+use dioxus_core::prelude::*;
+use dioxus_html as dioxus_elements;
+
+fn main() {
+    let mut dom = VirtualDom::new(App);
+    dom.rebuild().unwrap();
+    println!(
+        "{}",
+        dioxus_ssr::render_vdom(&dom, |c| c.newline(true).indent(true))
+    )
+}
+
+pub static App: FC<()> = |cx| {
+    cx.render(rsx!(
+        div {
+            class: "overflow-hidden"
+            ul {
+                {(0..10).map(|i| rsx!{ li { class: "flex flex-col", "entry: {i}"}})}
+            }
+            "hello world!"
+        }
+    ))
+};

+ 1 - 1
packages/ssr/examples/tofile.rs

@@ -13,7 +13,7 @@ fn main() {
     let mut file = File::create("example.html").unwrap();
 
     let mut dom = VirtualDom::new(App);
-    dom.rebuild_in_place().expect("failed to run virtualdom");
+    dom.rebuild().expect("failed to run virtualdom");
 
     file.write_fmt(format_args!(
         "{}",

+ 8 - 8
packages/ssr/src/lib.rs

@@ -46,7 +46,7 @@ pub fn render_vdom_scope(vdom: &VirtualDom, scope: ScopeId) -> Option<String> {
 /// ```ignore
 /// static App: FC<()> = |cx| cx.render(rsx!(div { "hello world" }));
 /// let mut vdom = VirtualDom::new(App);
-/// vdom.rebuild_in_place();
+/// vdom.rebuild();
 ///
 /// let renderer = TextRenderer::new(&vdom);
 /// let output = format!("{}", renderer);
@@ -74,7 +74,7 @@ impl<'a> TextRenderer<'a> {
     }
 
     fn html_render(&self, node: &VNode, f: &mut std::fmt::Formatter, il: u16) -> std::fmt::Result {
-        match &node.kind {
+        match &node {
             VNode::Text(text) => {
                 if self.cfg.indent {
                     for _ in 0..il {
@@ -286,28 +286,28 @@ mod tests {
     #[test]
     fn to_string_works() {
         let mut dom = VirtualDom::new(SIMPLE_APP);
-        dom.rebuild_in_place().expect("failed to run virtualdom");
+        dom.rebuild().expect("failed to run virtualdom");
         dbg!(render_vdom(&dom, |c| c));
     }
 
     #[test]
     fn hydration() {
         let mut dom = VirtualDom::new(NESTED_APP);
-        dom.rebuild_in_place().expect("failed to run virtualdom");
+        dom.rebuild().expect("failed to run virtualdom");
         dbg!(render_vdom(&dom, |c| c.pre_render(true)));
     }
 
     #[test]
     fn nested() {
         let mut dom = VirtualDom::new(NESTED_APP);
-        dom.rebuild_in_place().expect("failed to run virtualdom");
+        dom.rebuild().expect("failed to run virtualdom");
         dbg!(render_vdom(&dom, |c| c));
     }
 
     #[test]
     fn fragment_app() {
         let mut dom = VirtualDom::new(FRAGMENT_APP);
-        dom.rebuild_in_place().expect("failed to run virtualdom");
+        dom.rebuild().expect("failed to run virtualdom");
         dbg!(render_vdom(&dom, |c| c));
     }
 
@@ -319,7 +319,7 @@ mod tests {
         let mut file = File::create("index.html").unwrap();
 
         let mut dom = VirtualDom::new(SLIGHTLY_MORE_COMPLEX);
-        dom.rebuild_in_place().expect("failed to run virtualdom");
+        dom.rebuild().expect("failed to run virtualdom");
 
         file.write_fmt(format_args!(
             "{}",
@@ -337,7 +337,7 @@ mod tests {
         };
 
         let mut dom = VirtualDom::new(STLYE_APP);
-        dom.rebuild_in_place().expect("failed to run virtualdom");
+        dom.rebuild().expect("failed to run virtualdom");
         dbg!(render_vdom(&dom, |c| c));
     }
 }

+ 1 - 1
packages/web/examples/basic.rs

@@ -25,7 +25,7 @@ fn main() {
 }
 
 static App: FC<()> = |cx| {
-    let mut count = use_state(cx, || 0);
+    let mut count = use_state(cx, || 3);
 
     cx.render(rsx! {
         div {

+ 20 - 20
packages/web/src/dom.rs

@@ -84,8 +84,8 @@ impl WebsysDom {
                 DomEdit::PushRoot { id: root } => self.push(root),
                 DomEdit::PopRoot => self.pop(),
                 DomEdit::AppendChildren { many } => self.append_children(many),
-                DomEdit::ReplaceWith { n, m } => self.replace_with(n, m),
-                DomEdit::Remove => self.remove(),
+                DomEdit::ReplaceWith { m, root } => self.replace_with(m, root),
+                DomEdit::Remove { root } => self.remove(root),
                 DomEdit::RemoveAllChildren => self.remove_all_children(),
                 DomEdit::CreateTextNode { text, id } => self.create_text_node(text, id),
                 DomEdit::CreateElement { tag, id } => self.create_element(tag, None, id),
@@ -102,8 +102,8 @@ impl WebsysDom {
                 DomEdit::SetAttribute { field, value, ns } => self.set_attribute(field, value, ns),
                 DomEdit::RemoveAttribute { name } => self.remove_attribute(name),
 
-                DomEdit::InsertAfter { n } => self.insert_after(n),
-                DomEdit::InsertBefore { n } => self.insert_before(n),
+                DomEdit::InsertAfter { n, root } => self.insert_after(n, root),
+                DomEdit::InsertBefore { n, root } => self.insert_before(n, root),
             }
         }
     }
@@ -154,7 +154,7 @@ impl WebsysDom {
         }
     }
 
-    fn replace_with(&mut self, n: u32, m: u32) {
+    fn replace_with(&mut self, m: u32, root: u64) {
         log::debug!("Called [`replace_with`]");
 
         let mut new_nodes = vec![];
@@ -162,19 +162,11 @@ impl WebsysDom {
             new_nodes.push(self.stack.pop());
         }
 
-        let mut old_nodes = vec![];
-        for _ in 0..n {
-            old_nodes.push(self.stack.pop());
-        }
-
-        for node in &old_nodes[1..] {
-            node.dyn_ref::<Element>().unwrap().remove();
-        }
-
-        let old = old_nodes[0].clone();
+        let old = self.nodes[root as usize].as_ref().unwrap();
         let arr: js_sys::Array = new_nodes.iter().collect();
-        let el = old.dyn_into::<Element>().unwrap();
+        let el = old.dyn_ref::<Element>().unwrap();
         el.replace_with_with_node(&arr).unwrap();
+
         // let arr = js_sys::Array::from();
 
         // TODO: use different-sized replace withs
@@ -205,9 +197,17 @@ impl WebsysDom {
         // self.stack.push(new_node);
     }
 
-    fn remove(&mut self) {
+    fn remove(&mut self, root: u64) {
         log::debug!("Called [`remove`]");
-        todo!()
+
+        let node = self.nodes[root as usize].as_ref().unwrap();
+        if let Some(element) = node.dyn_ref::<Element>() {
+            element.remove();
+        } else {
+            if let Some(parent) = node.parent_node() {
+                parent.remove_child(&node).unwrap();
+            }
+        }
     }
 
     fn remove_all_children(&mut self) {
@@ -361,7 +361,7 @@ impl WebsysDom {
         }
     }
 
-    fn insert_after(&mut self, n: u32) {
+    fn insert_after(&mut self, n: u32, root: u64) {
         let mut new_nodes = vec![];
         for _ in 0..n {
             new_nodes.push(self.stack.pop());
@@ -380,7 +380,7 @@ impl WebsysDom {
         // let el = self.stack.top();
     }
 
-    fn insert_before(&mut self, n: u32) {
+    fn insert_before(&mut self, n: u32, root: u64) {
         let n = n as usize;
         let root = self
             .stack

+ 64 - 41
packages/web/src/lib.rs

@@ -1,30 +1,56 @@
 //! Dioxus WebSys
-//! --------------
-//! This crate implements a renderer of the Dioxus Virtual DOM for the web browser using Websys.
-
-/*
-From Google's guide on rAF and rIC:
---------
-
-If the callback is fired at the end of the frame, it will be scheduled to go after the current frame has been committed,
-which means that style changes will have been applied, and, importantly, layout calculated. If we make DOM changes inside
- of the idle callback, those layout calculations will be invalidated. If there are any kind of layout reads in the next
- frame, e.g. getBoundingClientRect, clientWidth, etc, the browser will have to perform a Forced Synchronous Layout,
- which is a potential performance bottleneck.
-
-Another reason not trigger DOM changes in the idle callback is that the time impact of changing the DOM is unpredictable,
-and as such we could easily go past the deadline the browser provided.
-
-The best practice is to only make DOM changes inside of a requestAnimationFrame callback, since it is scheduled by the
-browser with that type of work in mind. That means that our code will need to use a document fragment, which can then
-be appended in the next requestAnimationFrame callback. If you are using a VDOM library, you would use requestIdleCallback
-to make changes, but you would apply the DOM patches in the next requestAnimationFrame callback, not the idle callback.
-
-Essentially:
-------------
-- Do the VDOM work during the idlecallback
-- Do DOM work in the next requestAnimationFrame callback
-*/
+//!
+//! ## Overview
+//! ------------
+//! This crate implements a renderer of the Dioxus Virtual DOM for the web browser using WebSys. This web render for
+//! Dioxus is one of the more advanced renderers, supporting:
+//! - idle work
+//! - animations
+//! - jank-free rendering
+//! - noderefs
+//! - controlled components
+//! - re-hydration
+//! - and more.
+//!
+//! The actual implementation is farily thin, with the heavy lifting happening inside the Dioxus Core crate.
+//!
+//! To purview the examples, check of the root Dioxus crate - the examples in this crate are mostly meant to provide
+//! validation of websys-specific features and not the general use of Dioxus.
+//!
+//! ## RequestAnimationFrame and RequestIdleCallback
+//! ------------------------------------------------
+//! React implements "jank free rendering" by deliberately not blocking the browser's main thread. For large diffs, long
+//! running work, and integration with things like React-Three-Fiber, it's extremeley important to avoid blocking the
+//! main thread.
+//!
+//! React solves this problem by breaking up the rendering process into a "diff" phase and a "render" phase. In Dioxus,
+//! the diff phase is non-blocking, using "yield_now" to allow the browser to process other events. When the diff phase
+//! is  finally complete, the VirtualDOM will return a set of "Mutations" for this crate to apply.
+//!
+//! Here, we schedule the "diff" phase during the browser's idle period, achieved by calling RequestIdleCallback and then
+//! setting a timeout from the that completes when the idleperiod is over. Then, we call requestAnimationFrame
+//!
+//!     From Google's guide on rAF and rIC:
+//!     -----------------------------------
+//!
+//!     If the callback is fired at the end of the frame, it will be scheduled to go after the current frame has been committed,
+//!     which means that style changes will have been applied, and, importantly, layout calculated. If we make DOM changes inside
+//!      of the idle callback, those layout calculations will be invalidated. If there are any kind of layout reads in the next
+//!      frame, e.g. getBoundingClientRect, clientWidth, etc, the browser will have to perform a Forced Synchronous Layout,
+//!      which is a potential performance bottleneck.
+//!
+//!     Another reason not trigger DOM changes in the idle callback is that the time impact of changing the DOM is unpredictable,
+//!     and as such we could easily go past the deadline the browser provided.
+//!
+//!     The best practice is to only make DOM changes inside of a requestAnimationFrame callback, since it is scheduled by the
+//!     browser with that type of work in mind. That means that our code will need to use a document fragment, which can then
+//!     be appended in the next requestAnimationFrame callback. If you are using a VDOM library, you would use requestIdleCallback
+//!     to make changes, but you would apply the DOM patches in the next requestAnimationFrame callback, not the idle callback.
+//!
+//!     Essentially:
+//!     ------------
+//!     - Do the VDOM work during the idlecallback
+//!     - Do DOM work in the next requestAnimationFrame callback
 
 use std::rc::Rc;
 
@@ -103,20 +129,22 @@ pub async fn run_with_props<T: Properties + 'static>(
 ) -> Result<()> {
     let mut dom = VirtualDom::new_with_props(root, root_props);
 
-    let root_el = load_document().get_element_by_id(&cfg.rootname).unwrap();
+    let hydrating = cfg.hydrate;
 
+    let root_el = load_document().get_element_by_id(&cfg.rootname).unwrap();
     let tasks = dom.get_event_sender();
+    let sender_callback = Rc::new(move |event| tasks.unbounded_send(event).unwrap());
+
+    let mut websys_dom = dom::WebsysDom::new(root_el, cfg, sender_callback);
 
-    // initialize the virtualdom first
-    // if cfg.hydrate {
-    //     dom.rebuild_in_place()?;
-    // }
+    let mut mutations = dom.rebuild().unwrap();
+    log::info!("Mutations: {:#?}", mutations);
 
-    let mut websys_dom = dom::WebsysDom::new(
-        root_el,
-        cfg,
-        Rc::new(move |event| tasks.unbounded_send(event).unwrap()),
-    );
+    // hydrating is simply running the dom for a single render. If the page is already written, then the corresponding
+    // ElementIds should already line up because the web_sys dom has already loaded elements with the DioxusID into memory
+    if !hydrating {
+        websys_dom.process_edits(&mut mutations.edits);
+    }
 
     loop {
         let deadline = gloo_timers::future::TimeoutFuture::new(16);
@@ -127,8 +155,3 @@ pub async fn run_with_props<T: Properties + 'static>(
 
     Ok(())
 }
-
-struct HydrationNode {
-    id: usize,
-    node: Node,
-}