wip: it works but the page is backwards
This commit is contained in:
1 parent
a0a977a12d
commit
cdcd8611e8
19 files changed
+239
-349
No files matched your search
@@ -1,16 +1 @@
|
|||||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
fn main() {}
|
||||||
|
|
||||||
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);
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#![allow(non_snake_case, non_upper_case_globals)]
|
||||||
//! This benchmark tests just the overhead of Dioxus itself.
|
//! 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
|
//! 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 3ms to create 1_000 rows
|
||||||
//! - Dioxus takes 30ms to create 10_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.
|
//! 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::{criterion_group, criterion_main, Criterion};
|
||||||
|
|
||||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
|
||||||
use dioxus_core as dioxus;
|
use dioxus_core as dioxus;
|
||||||
use dioxus_core::prelude::*;
|
use dioxus_core::prelude::*;
|
||||||
use dioxus_html as dioxus_elements;
|
use dioxus_html as dioxus_elements;
|
||||||
@@ -58,11 +57,12 @@ struct RowProps {
|
|||||||
label: Label,
|
label: Label,
|
||||||
}
|
}
|
||||||
fn Row<'a>(cx: Context<'a, RowProps>) -> DomTree {
|
fn Row<'a>(cx: Context<'a, RowProps>) -> DomTree {
|
||||||
|
let [adj, col, noun] = cx.label.0;
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
tr {
|
tr {
|
||||||
td { class:"col-md-1", "{cx.row_id}" }
|
td { class:"col-md-1", "{cx.row_id}" }
|
||||||
td { class:"col-md-1", onclick: move |_| { /* run onselect */ }
|
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"
|
td { class: "col-md-1"
|
||||||
a { class: "remove", onclick: move |_| {/* remove */}
|
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] = &[
|
static ADJECTIVES: &[&str] = &[
|
||||||
"pretty",
|
"pretty",
|
||||||
|
|||||||
@@ -6,17 +6,16 @@ pub static Example: FC<()> = |cx| {
|
|||||||
let list = (0..10).map(|f| LazyNodes::new(move |f| todo!()));
|
let list = (0..10).map(|f| LazyNodes::new(move |f| todo!()));
|
||||||
|
|
||||||
cx.render(LazyNodes::new(move |cx| {
|
cx.render(LazyNodes::new(move |cx| {
|
||||||
let bump = cx.bump();
|
|
||||||
cx.raw_element(
|
cx.raw_element(
|
||||||
"div",
|
"div",
|
||||||
None,
|
None,
|
||||||
&mut [],
|
[],
|
||||||
&mut [],
|
[],
|
||||||
cx.bump().alloc([
|
[
|
||||||
cx.text(format_args!("hello")),
|
cx.text(format_args!("hello")),
|
||||||
cx.text(format_args!("hello")),
|
cx.text(format_args!("hello")),
|
||||||
cx.fragment_from_iter(list),
|
cx.fragment_from_iter(list),
|
||||||
]),
|
],
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
}))
|
}))
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
|
|
||||||
// let g = rsx! {
|
|
||||||
// Fragment {
|
|
||||||
// // div {}
|
|
||||||
// // div {}
|
|
||||||
// // div {}
|
|
||||||
// // div {}
|
|
||||||
// // div {}
|
|
||||||
// // div {}
|
|
||||||
// // div {}
|
|
||||||
// // div {}
|
|
||||||
// // div {}
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
}
|
|
||||||
@@ -1,31 +1,27 @@
|
|||||||
fn main() {}
|
|
||||||
|
|
||||||
use dioxus_core::prelude::*;
|
use dioxus_core::prelude::*;
|
||||||
|
|
||||||
fn App(cx: Context<()>) -> DomTree {
|
fn main() {}
|
||||||
//
|
|
||||||
|
fn app(cx: Context<()>) -> DomTree {
|
||||||
let vak = use_suspense(
|
let vak = use_suspense(
|
||||||
cx,
|
cx,
|
||||||
|| async {},
|
|| async {},
|
||||||
|c, res| {
|
|c, _res| c.render(LazyNodes::new(move |f| f.text(format_args!("")))),
|
||||||
//
|
|
||||||
c.render(LazyNodes::new(move |f| f.text(format_args!(""))))
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let d1 = cx.render(LazyNodes::new(move |f| {
|
let d1 = cx.render(LazyNodes::new(move |f| {
|
||||||
f.raw_element(
|
f.raw_element(
|
||||||
"div",
|
"div",
|
||||||
None,
|
None,
|
||||||
&mut [],
|
[],
|
||||||
&[],
|
[],
|
||||||
f.bump().alloc([
|
[
|
||||||
f.fragment_from_iter(vak),
|
f.fragment_from_iter(vak),
|
||||||
f.text(format_args!("")),
|
f.text(format_args!("")),
|
||||||
f.text(format_args!("")),
|
f.text(format_args!("")),
|
||||||
f.text(format_args!("")),
|
f.text(format_args!("")),
|
||||||
f.text(format_args!("")),
|
f.text(format_args!("")),
|
||||||
]),
|
],
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
}));
|
}));
|
||||||
@@ -34,15 +30,15 @@ fn App(cx: Context<()>) -> DomTree {
|
|||||||
f.raw_element(
|
f.raw_element(
|
||||||
"div",
|
"div",
|
||||||
None,
|
None,
|
||||||
&mut [],
|
[],
|
||||||
&[],
|
[],
|
||||||
f.bump().alloc([
|
[
|
||||||
f.text(format_args!("")),
|
f.text(format_args!("")),
|
||||||
f.text(format_args!("")),
|
f.text(format_args!("")),
|
||||||
f.text(format_args!("")),
|
f.text(format_args!("")),
|
||||||
f.text(format_args!("")),
|
f.text(format_args!("")),
|
||||||
f.fragment_from_iter(d1),
|
d1.unwrap(),
|
||||||
]),
|
],
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
}))
|
}))
|
||||||
|
|||||||
@@ -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
@@ -89,7 +89,6 @@
|
|||||||
//! - https://hacks.mozilla.org/2019/03/fast-bump-allocated-virtual-doms-with-rust-and-wasm/
|
//! - https://hacks.mozilla.org/2019/03/fast-bump-allocated-virtual-doms-with-rust-and-wasm/
|
||||||
|
|
||||||
use crate::{arena::SharedResources, innerlude::*};
|
use crate::{arena::SharedResources, innerlude::*};
|
||||||
use futures_util::{Future, FutureExt};
|
|
||||||
use fxhash::{FxHashMap, FxHashSet};
|
use fxhash::{FxHashMap, FxHashSet};
|
||||||
use DomEdit::*;
|
use DomEdit::*;
|
||||||
|
|
||||||
@@ -281,19 +280,20 @@ impl<'bump> DiffMachine<'bump> {
|
|||||||
} = element;
|
} = element;
|
||||||
|
|
||||||
let real_id = self.vdom.reserve_node();
|
let real_id = self.vdom.reserve_node();
|
||||||
|
dom_id.set(Some(real_id));
|
||||||
|
|
||||||
self.mutations.create_element(tag_name, *namespace, real_id);
|
self.mutations.create_element(tag_name, *namespace, real_id);
|
||||||
|
|
||||||
self.stack.add_child_count(1);
|
self.stack.add_child_count(1);
|
||||||
|
|
||||||
dom_id.set(Some(real_id));
|
let cur_scope_id = self.stack.current_scope().unwrap();
|
||||||
|
let scope = self.vdom.get_scope(cur_scope_id).unwrap();
|
||||||
let cur_scope = self.stack.current_scope().unwrap();
|
|
||||||
|
|
||||||
listeners.iter().for_each(|listener| {
|
listeners.iter().for_each(|listener| {
|
||||||
self.fix_listener(listener);
|
self.attach_listener_to_scope(listener, scope);
|
||||||
listener.mounted_node.set(Some(real_id));
|
listener.mounted_node.set(Some(real_id));
|
||||||
self.mutations
|
self.mutations
|
||||||
.new_event_listener(listener, cur_scope.clone());
|
.new_event_listener(listener, cur_scope_id.clone());
|
||||||
});
|
});
|
||||||
|
|
||||||
for attr in *attributes {
|
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)
|
// 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
|
// 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() {
|
if old.listeners.len() == new.listeners.len() {
|
||||||
for (old_l, new_l) in old.listeners.iter().zip(new.listeners.iter()) {
|
for (old_l, new_l) in old.listeners.iter().zip(new.listeners.iter()) {
|
||||||
if old_l.event != new_l.event {
|
if old_l.event != new_l.event {
|
||||||
please_commit(&mut self.mutations.edits);
|
please_commit(&mut self.mutations.edits);
|
||||||
self.mutations.remove_event_listener(old_l.event);
|
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());
|
new_l.mounted_node.set(old_l.mounted_node.get());
|
||||||
self.fix_listener(new_l);
|
self.attach_listener_to_scope(new_l, scope);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
please_commit(&mut self.mutations.edits);
|
please_commit(&mut self.mutations.edits);
|
||||||
@@ -484,10 +487,8 @@ impl<'bump> DiffMachine<'bump> {
|
|||||||
}
|
}
|
||||||
for listener in new.listeners {
|
for listener in new.listeners {
|
||||||
listener.mounted_node.set(Some(root));
|
listener.mounted_node.set(Some(root));
|
||||||
self.mutations.new_event_listener(listener, cur_scope);
|
self.mutations.new_event_listener(listener, cur_scope_id);
|
||||||
|
self.attach_listener_to_scope(listener, scope);
|
||||||
// Make sure the listener gets attached to the scope list
|
|
||||||
self.fix_listener(listener);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1082,13 +1083,10 @@ impl<'bump> DiffMachine<'bump> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fix_listener<'a>(&mut self, listener: &'a Listener<'a>) {
|
/// Adds a listener closure to a scope during diff.
|
||||||
let scope_id = self.stack.current_scope();
|
fn attach_listener_to_scope<'a>(&mut self, listener: &'a Listener<'a>, scope: &Scope) {
|
||||||
if let Some(scope_id) = scope_id {
|
let mut queue = scope.listeners.borrow_mut();
|
||||||
let scope = self.vdom.get_scope(scope_id).unwrap();
|
let long_listener: &'a Listener<'static> = unsafe { std::mem::transmute(listener) };
|
||||||
let mut queue = scope.listeners.borrow_mut();
|
queue.push(long_listener as *const _)
|
||||||
let long_listener: &'a Listener<'static> = unsafe { std::mem::transmute(listener) };
|
|
||||||
queue.push(long_listener as *const _)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -113,7 +113,6 @@ impl Scope {
|
|||||||
child_nodes: ScopeChildren,
|
child_nodes: ScopeChildren,
|
||||||
) {
|
) {
|
||||||
self.caller = caller;
|
self.caller = caller;
|
||||||
// let child_nodes = unsafe { std::mem::transmute(child_nodes) };
|
|
||||||
let child_nodes = unsafe { child_nodes.extend_lifetime() };
|
let child_nodes = unsafe { child_nodes.extend_lifetime() };
|
||||||
self.child_nodes = child_nodes;
|
self.child_nodes = child_nodes;
|
||||||
}
|
}
|
||||||
@@ -237,8 +236,8 @@ impl Scope {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn consume_garbage(&self) -> Vec<&VNode> {
|
pub fn consume_garbage(&self) -> Vec<&VNode> {
|
||||||
let mut garbage = self.pending_garbage.borrow_mut();
|
self.pending_garbage
|
||||||
garbage
|
.borrow_mut()
|
||||||
.drain(..)
|
.drain(..)
|
||||||
.map(|node| {
|
.map(|node| {
|
||||||
// safety: scopes cannot cycle without their garbage being collected. these nodes are safe
|
// safety: scopes cannot cycle without their garbage being collected. these nodes are safe
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ use dioxus_core as dioxus;
|
|||||||
use dioxus_html as dioxus_elements;
|
use dioxus_html as dioxus_elements;
|
||||||
use DomEdit::*;
|
use DomEdit::*;
|
||||||
|
|
||||||
|
const LOGGING_ENABLED: bool = false;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_original_diff() {
|
fn test_original_diff() {
|
||||||
static App: FC<()> = |cx| {
|
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 mut dom = VirtualDom::new(App);
|
||||||
let mutations = dom.rebuild_async().await.unwrap();
|
let mutations = dom.rebuild_async().await.unwrap();
|
||||||
assert_eq!(
|
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 mut dom = VirtualDom::new(App);
|
||||||
let mutations = dom.rebuild_async().await.unwrap();
|
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 mut dom = VirtualDom::new(App);
|
||||||
let mutations = dom.rebuild_async().await.unwrap();
|
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 mut dom = VirtualDom::new(App);
|
||||||
let mutations = dom.rebuild_async().await.unwrap();
|
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 mut dom = VirtualDom::new(App);
|
||||||
let mutations = dom.rebuild_async().await.unwrap();
|
let mutations = dom.rebuild_async().await.unwrap();
|
||||||
@@ -249,7 +251,7 @@ async fn suspended() {
|
|||||||
cx.render(rsx! { {val} })
|
cx.render(rsx! { {val} })
|
||||||
};
|
};
|
||||||
|
|
||||||
test_logging::set_up_logging();
|
test_logging::set_up_logging(LOGGING_ENABLED);
|
||||||
|
|
||||||
let mut dom = VirtualDom::new(App);
|
let mut dom = VirtualDom::new(App);
|
||||||
let mutations = dom.rebuild_async().await.unwrap();
|
let mutations = dom.rebuild_async().await.unwrap();
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ mod test_logging;
|
|||||||
use dioxus_core as dioxus;
|
use dioxus_core as dioxus;
|
||||||
use dioxus_html as dioxus_elements;
|
use dioxus_html as dioxus_elements;
|
||||||
|
|
||||||
|
const LOGGING_ENABLED: bool = false;
|
||||||
|
|
||||||
#[async_std::test]
|
#[async_std::test]
|
||||||
async fn test_iterative_create_components() {
|
async fn test_iterative_create_components() {
|
||||||
static App: FC<()> = |cx| {
|
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);
|
let mut dom = VirtualDom::new(App);
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,7 @@
|
|||||||
//! Diffing Tests
|
//! 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 bumpalo::Bump;
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
|
||||||
use dioxus::{
|
use dioxus::{
|
||||||
arena::SharedResources, diff::DiffMachine, prelude::*, DiffInstruction, DomEdit, MountType,
|
arena::SharedResources, diff::DiffMachine, prelude::*, DiffInstruction, DomEdit, MountType,
|
||||||
};
|
};
|
||||||
@@ -18,7 +13,7 @@ use DomEdit::*;
|
|||||||
|
|
||||||
// logging is wired up to the test harness
|
// logging is wired up to the test harness
|
||||||
// feel free to enable while debugging
|
// feel free to enable while debugging
|
||||||
const IS_LOGGING_ENABLED: bool = true;
|
const IS_LOGGING_ENABLED: bool = false;
|
||||||
|
|
||||||
struct TestDom {
|
struct TestDom {
|
||||||
bump: Bump,
|
bump: Bump,
|
||||||
@@ -26,9 +21,7 @@ struct TestDom {
|
|||||||
}
|
}
|
||||||
impl TestDom {
|
impl TestDom {
|
||||||
fn new() -> TestDom {
|
fn new() -> TestDom {
|
||||||
if IS_LOGGING_ENABLED {
|
test_logging::set_up_logging(IS_LOGGING_ENABLED);
|
||||||
test_logging::set_up_logging();
|
|
||||||
}
|
|
||||||
let bump = Bump::new();
|
let bump = Bump::new();
|
||||||
let resources = SharedResources::new();
|
let resources = SharedResources::new();
|
||||||
TestDom { bump, resources }
|
TestDom { bump, resources }
|
||||||
@@ -82,8 +75,6 @@ impl TestDom {
|
|||||||
|
|
||||||
let new = self.bump.alloc(self.render(right));
|
let new = self.bump.alloc(self.render(right));
|
||||||
|
|
||||||
// let mut create_edits = Vec::new();
|
|
||||||
|
|
||||||
let mut machine = DiffMachine::new_headless(&self.resources);
|
let mut machine = DiffMachine::new_headless(&self.resources);
|
||||||
|
|
||||||
machine.stack.create_node(old, MountType::Append);
|
machine.stack.create_node(old, MountType::Append);
|
||||||
@@ -343,16 +334,31 @@ fn two_fragments_with_differrent_elements_are_differet() {
|
|||||||
let dom = TestDom::new();
|
let dom = TestDom::new();
|
||||||
|
|
||||||
let left = rsx!(
|
let left = rsx!(
|
||||||
{ (0..2).map(|f| rsx! { div { }} ) }
|
{ (0..2).map(|_| rsx! { div { }} ) }
|
||||||
p {}
|
p {}
|
||||||
);
|
);
|
||||||
let right = rsx!(
|
let right = rsx!(
|
||||||
{ (0..5).map(|f| rsx! (h1 { }) ) }
|
{ (0..5).map(|_| rsx! (h1 { }) ) }
|
||||||
p {}
|
p {}
|
||||||
);
|
);
|
||||||
|
|
||||||
let edits = dom.lazy_diff(left, right);
|
let (create, changes) = dom.lazy_diff(left, right);
|
||||||
log::debug!("{:#?}", &edits);
|
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
|
/// 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
|
/// Should result in moves, but not removals or additions
|
||||||
#[test]
|
#[test]
|
||||||
fn keyed_diffing_out_of_order() {
|
fn keyed_diffing_out_of_order() {
|
||||||
@@ -477,8 +475,12 @@ fn keyed_diffing_out_of_order() {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
let edits = dom.lazy_diff(left, right);
|
let (_, changes) = dom.lazy_diff(left, right);
|
||||||
log::debug!("{:?}", &edits.1);
|
log::debug!("{:?}", &changes);
|
||||||
|
assert_eq!(
|
||||||
|
changes.edits,
|
||||||
|
[PushRoot { id: 6 }, InsertBefore { root: 4, n: 1 }]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Should result in moves only
|
/// Should result in moves only
|
||||||
@@ -655,21 +657,24 @@ fn keyed_diffing_additions_and_moves_on_ends() {
|
|||||||
|
|
||||||
let right = rsx!({
|
let right = rsx!({
|
||||||
[/**/ 7, 4, 5, 6, 11, 12 /**/].iter().map(|f| {
|
[/**/ 7, 4, 5, 6, 11, 12 /**/].iter().map(|f| {
|
||||||
// [/**/ 8, 7, 4, 5, 6, 9, 10 /**/].iter().map(|f| {
|
|
||||||
rsx! { div { key: "{f}" }}
|
rsx! { div { key: "{f}" }}
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
let (_, change) = dom.lazy_diff(left, right);
|
let (_, change) = dom.lazy_diff(left, right);
|
||||||
log::debug!("{:?}", change);
|
log::debug!("{:?}", change);
|
||||||
// assert_eq!(
|
assert_eq!(
|
||||||
// change.edits,
|
change.edits,
|
||||||
// [
|
[
|
||||||
// CreateElement { id: 5, tag: "div" },
|
// create 11, 12
|
||||||
// CreateElement { id: 6, tag: "div" },
|
CreateElement { tag: "div", id: 4 },
|
||||||
// InsertAfter { n: 2, root: 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]
|
#[test]
|
||||||
@@ -688,16 +693,25 @@ fn keyed_diffing_additions_and_moves_in_middle() {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// LIS: 4, 5, 6
|
||||||
let (_, change) = dom.lazy_diff(left, right);
|
let (_, change) = dom.lazy_diff(left, right);
|
||||||
log::debug!("{:?}", change);
|
log::debug!("{:#?}", change);
|
||||||
// assert_eq!(
|
assert_eq!(
|
||||||
// change.edits,
|
change.edits,
|
||||||
// [
|
[
|
||||||
// CreateElement { id: 5, tag: "div" },
|
// create 13, 17
|
||||||
// CreateElement { id: 6, tag: "div" },
|
CreateElement { tag: "div", id: 4 },
|
||||||
// InsertAfter { n: 2, root: 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]
|
#[test]
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
pub fn set_up_logging() {
|
pub fn set_up_logging(enabled: bool) {
|
||||||
use fern::colors::{Color, ColoredLevelConfig};
|
use fern::colors::{Color, ColoredLevelConfig};
|
||||||
use log::debug;
|
use log::debug;
|
||||||
|
|
||||||
|
if !enabled {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// configure colors for the whole line
|
// configure colors for the whole line
|
||||||
let colors_line = ColoredLevelConfig::new()
|
let colors_line = ColoredLevelConfig::new()
|
||||||
.error(Color::Red)
|
.error(Color::Red)
|
||||||
|
|||||||
@@ -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 {
|
trait AsAttributeValue: Sized {
|
||||||
fn into_attribute_value<'a>(self, cx: NodeFactory<'a>) -> AttributeValue<'a>;
|
fn into_attribute_value<'a>(self, cx: NodeFactory<'a>) -> AttributeValue<'a>;
|
||||||
@@ -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!"
|
||||||
|
}
|
||||||
|
))
|
||||||
|
};
|
||||||
@@ -13,7 +13,7 @@ fn main() {
|
|||||||
let mut file = File::create("example.html").unwrap();
|
let mut file = File::create("example.html").unwrap();
|
||||||
|
|
||||||
let mut dom = VirtualDom::new(App);
|
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!(
|
file.write_fmt(format_args!(
|
||||||
"{}",
|
"{}",
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ pub fn render_vdom_scope(vdom: &VirtualDom, scope: ScopeId) -> Option<String> {
|
|||||||
/// ```ignore
|
/// ```ignore
|
||||||
/// static App: FC<()> = |cx| cx.render(rsx!(div { "hello world" }));
|
/// static App: FC<()> = |cx| cx.render(rsx!(div { "hello world" }));
|
||||||
/// let mut vdom = VirtualDom::new(App);
|
/// let mut vdom = VirtualDom::new(App);
|
||||||
/// vdom.rebuild_in_place();
|
/// vdom.rebuild();
|
||||||
///
|
///
|
||||||
/// let renderer = TextRenderer::new(&vdom);
|
/// let renderer = TextRenderer::new(&vdom);
|
||||||
/// let output = format!("{}", renderer);
|
/// 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 {
|
fn html_render(&self, node: &VNode, f: &mut std::fmt::Formatter, il: u16) -> std::fmt::Result {
|
||||||
match &node.kind {
|
match &node {
|
||||||
VNode::Text(text) => {
|
VNode::Text(text) => {
|
||||||
if self.cfg.indent {
|
if self.cfg.indent {
|
||||||
for _ in 0..il {
|
for _ in 0..il {
|
||||||
@@ -286,28 +286,28 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn to_string_works() {
|
fn to_string_works() {
|
||||||
let mut dom = VirtualDom::new(SIMPLE_APP);
|
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));
|
dbg!(render_vdom(&dom, |c| c));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn hydration() {
|
fn hydration() {
|
||||||
let mut dom = VirtualDom::new(NESTED_APP);
|
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)));
|
dbg!(render_vdom(&dom, |c| c.pre_render(true)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn nested() {
|
fn nested() {
|
||||||
let mut dom = VirtualDom::new(NESTED_APP);
|
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));
|
dbg!(render_vdom(&dom, |c| c));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn fragment_app() {
|
fn fragment_app() {
|
||||||
let mut dom = VirtualDom::new(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));
|
dbg!(render_vdom(&dom, |c| c));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -319,7 +319,7 @@ mod tests {
|
|||||||
let mut file = File::create("index.html").unwrap();
|
let mut file = File::create("index.html").unwrap();
|
||||||
|
|
||||||
let mut dom = VirtualDom::new(SLIGHTLY_MORE_COMPLEX);
|
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!(
|
file.write_fmt(format_args!(
|
||||||
"{}",
|
"{}",
|
||||||
@@ -337,7 +337,7 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut dom = VirtualDom::new(STLYE_APP);
|
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));
|
dbg!(render_vdom(&dom, |c| c));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -25,7 +25,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static App: FC<()> = |cx| {
|
static App: FC<()> = |cx| {
|
||||||
let mut count = use_state(cx, || 0);
|
let mut count = use_state(cx, || 3);
|
||||||
|
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div {
|
div {
|
||||||
|
|||||||
+20
-20
@@ -84,8 +84,8 @@ impl WebsysDom {
|
|||||||
DomEdit::PushRoot { id: root } => self.push(root),
|
DomEdit::PushRoot { id: root } => self.push(root),
|
||||||
DomEdit::PopRoot => self.pop(),
|
DomEdit::PopRoot => self.pop(),
|
||||||
DomEdit::AppendChildren { many } => self.append_children(many),
|
DomEdit::AppendChildren { many } => self.append_children(many),
|
||||||
DomEdit::ReplaceWith { n, m } => self.replace_with(n, m),
|
DomEdit::ReplaceWith { m, root } => self.replace_with(m, root),
|
||||||
DomEdit::Remove => self.remove(),
|
DomEdit::Remove { root } => self.remove(root),
|
||||||
DomEdit::RemoveAllChildren => self.remove_all_children(),
|
DomEdit::RemoveAllChildren => self.remove_all_children(),
|
||||||
DomEdit::CreateTextNode { text, id } => self.create_text_node(text, id),
|
DomEdit::CreateTextNode { text, id } => self.create_text_node(text, id),
|
||||||
DomEdit::CreateElement { tag, id } => self.create_element(tag, None, 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::SetAttribute { field, value, ns } => self.set_attribute(field, value, ns),
|
||||||
DomEdit::RemoveAttribute { name } => self.remove_attribute(name),
|
DomEdit::RemoveAttribute { name } => self.remove_attribute(name),
|
||||||
|
|
||||||
DomEdit::InsertAfter { n } => self.insert_after(n),
|
DomEdit::InsertAfter { n, root } => self.insert_after(n, root),
|
||||||
DomEdit::InsertBefore { n } => self.insert_before(n),
|
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`]");
|
log::debug!("Called [`replace_with`]");
|
||||||
|
|
||||||
let mut new_nodes = vec![];
|
let mut new_nodes = vec![];
|
||||||
@@ -162,19 +162,11 @@ impl WebsysDom {
|
|||||||
new_nodes.push(self.stack.pop());
|
new_nodes.push(self.stack.pop());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut old_nodes = vec![];
|
let old = self.nodes[root as usize].as_ref().unwrap();
|
||||||
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 arr: js_sys::Array = new_nodes.iter().collect();
|
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();
|
el.replace_with_with_node(&arr).unwrap();
|
||||||
|
|
||||||
// let arr = js_sys::Array::from();
|
// let arr = js_sys::Array::from();
|
||||||
|
|
||||||
// TODO: use different-sized replace withs
|
// TODO: use different-sized replace withs
|
||||||
@@ -205,9 +197,17 @@ impl WebsysDom {
|
|||||||
// self.stack.push(new_node);
|
// self.stack.push(new_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove(&mut self) {
|
fn remove(&mut self, root: u64) {
|
||||||
log::debug!("Called [`remove`]");
|
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) {
|
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![];
|
let mut new_nodes = vec![];
|
||||||
for _ in 0..n {
|
for _ in 0..n {
|
||||||
new_nodes.push(self.stack.pop());
|
new_nodes.push(self.stack.pop());
|
||||||
@@ -380,7 +380,7 @@ impl WebsysDom {
|
|||||||
// let el = self.stack.top();
|
// 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 n = n as usize;
|
||||||
let root = self
|
let root = self
|
||||||
.stack
|
.stack
|
||||||
|
|||||||
+64
-41
@@ -1,30 +1,56 @@
|
|||||||
//! Dioxus WebSys
|
//! Dioxus WebSys
|
||||||
//! --------------
|
//!
|
||||||
//! This crate implements a renderer of the Dioxus Virtual DOM for the web browser using Websys.
|
//! ## Overview
|
||||||
|
//! ------------
|
||||||
/*
|
//! This crate implements a renderer of the Dioxus Virtual DOM for the web browser using WebSys. This web render for
|
||||||
From Google's guide on rAF and rIC:
|
//! Dioxus is one of the more advanced renderers, supporting:
|
||||||
--------
|
//! - idle work
|
||||||
|
//! - animations
|
||||||
If the callback is fired at the end of the frame, it will be scheduled to go after the current frame has been committed,
|
//! - jank-free rendering
|
||||||
which means that style changes will have been applied, and, importantly, layout calculated. If we make DOM changes inside
|
//! - noderefs
|
||||||
of the idle callback, those layout calculations will be invalidated. If there are any kind of layout reads in the next
|
//! - controlled components
|
||||||
frame, e.g. getBoundingClientRect, clientWidth, etc, the browser will have to perform a Forced Synchronous Layout,
|
//! - re-hydration
|
||||||
which is a potential performance bottleneck.
|
//! - and more.
|
||||||
|
//!
|
||||||
Another reason not trigger DOM changes in the idle callback is that the time impact of changing the DOM is unpredictable,
|
//! The actual implementation is farily thin, with the heavy lifting happening inside the Dioxus Core crate.
|
||||||
and as such we could easily go past the deadline the browser provided.
|
//!
|
||||||
|
//! To purview the examples, check of the root Dioxus crate - the examples in this crate are mostly meant to provide
|
||||||
The best practice is to only make DOM changes inside of a requestAnimationFrame callback, since it is scheduled by the
|
//! validation of websys-specific features and not the general use of Dioxus.
|
||||||
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
|
//! ## RequestAnimationFrame and RequestIdleCallback
|
||||||
to make changes, but you would apply the DOM patches in the next requestAnimationFrame callback, not the idle callback.
|
//! ------------------------------------------------
|
||||||
|
//! React implements "jank free rendering" by deliberately not blocking the browser's main thread. For large diffs, long
|
||||||
Essentially:
|
//! running work, and integration with things like React-Three-Fiber, it's extremeley important to avoid blocking the
|
||||||
------------
|
//! main thread.
|
||||||
- Do the VDOM work during the idlecallback
|
//!
|
||||||
- Do DOM work in the next requestAnimationFrame callback
|
//! 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;
|
use std::rc::Rc;
|
||||||
|
|
||||||
@@ -103,20 +129,22 @@ pub async fn run_with_props<T: Properties + 'static>(
|
|||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let mut dom = VirtualDom::new_with_props(root, root_props);
|
let mut dom = VirtualDom::new_with_props(root, root_props);
|
||||||
|
|
||||||
|
let hydrating = cfg.hydrate;
|
||||||
|
|
||||||
let root_el = load_document().get_element_by_id(&cfg.rootname).unwrap();
|
let root_el = load_document().get_element_by_id(&cfg.rootname).unwrap();
|
||||||
|
|
||||||
let tasks = dom.get_event_sender();
|
let tasks = dom.get_event_sender();
|
||||||
|
let sender_callback = Rc::new(move |event| tasks.unbounded_send(event).unwrap());
|
||||||
|
|
||||||
// initialize the virtualdom first
|
let mut websys_dom = dom::WebsysDom::new(root_el, cfg, sender_callback);
|
||||||
// if cfg.hydrate {
|
|
||||||
// dom.rebuild_in_place()?;
|
|
||||||
// }
|
|
||||||
|
|
||||||
let mut websys_dom = dom::WebsysDom::new(
|
let mut mutations = dom.rebuild().unwrap();
|
||||||
root_el,
|
log::info!("Mutations: {:#?}", mutations);
|
||||||
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 {
|
loop {
|
||||||
let deadline = gloo_timers::future::TimeoutFuture::new(16);
|
let deadline = gloo_timers::future::TimeoutFuture::new(16);
|
||||||
@@ -127,8 +155,3 @@ pub async fn run_with_props<T: Properties + 'static>(
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
struct HydrationNode {
|
|
||||||
id: usize,
|
|
||||||
node: Node,
|
|
||||||
}
|
|
||||||
Reference in new issue
Block a user