wip: it works but the page is backwards

This commit is contained in:
Jonathan Kelley committed 2021-08-24 12:43:46 -04:00
1 parent a0a977a12d
commit cdcd8611e8
19 files changed
+239 -349

No files matched your search

+1 -16
View File
@@ -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
View File
@@ -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
View File
@@ -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,
)
}))
-18
View File
@@ -1,18 +0,0 @@
fn main() {
// let g = rsx! {
// Fragment {
// // div {}
// // div {}
// // div {}
// // div {}
// // div {}
// // div {}
// // div {}
// // div {}
// // div {}
// }
// };
}
+13 -17
View File
@@ -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,
)
}))
-137
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)