wip: rethinking stack machine

This commit is contained in:
Jonathan Kelley 2021-06-19 20:31:25 -04:00
parent 70cd46dbb2
commit 62ae5d3bb9
7 changed files with 229 additions and 86 deletions

View File

@ -50,3 +50,10 @@ crates.io
utilties
js
Dyon
ssr
docusarus
docsite
dom
textarea
noderefs
wasm

View File

@ -1,60 +1,99 @@
fn main() {}
//! A tour of the rsx! macro
//! ------------------------
//!
//! This example serves as an informal quick reference of all the things that the rsx! macro can do.
//!
//! A full in-depth reference guide is available at: https://www.notion.so/rsx-macro-basics-ef6e367dec124f4784e736d91b0d0b19
//!
//! ## Topics
//!
//!
//!
//! ### Elements
//! - Create any element from its tag
//! - Accept compile-safe attributes for each tag
//! - Display documentation for elements
//! - Arguments instead of String
//! - Text
//! - Inline Styles
//!
//! ## General Concepts
//! - Iterators
//! - Keys
//! - Match statements
//! - Conditional Rendering
//!
//! ### Events
//! - Handle events with the "onXYZ" syntax
//! - Closures can capture their environment with the 'a lifetime
//!
//!
//! ### Components
//! - Components can be made by specifying the name
//! - Components can be referenced by path
//! - Components may have optional parameters
//! - Components may have their properties specified by spread syntax
//! - Components may accept child nodes
//! - Components that accept "onXYZ" get those closures bump allocated
//!
//! ### Fragments
//! - Allow fragments using the built-in `Fragment` component
//! - Accept a list of vnodes as children for a Fragment component
//! - Allow keyed fragments in iterators
//! - Allow top-level fragments
//!
fn main() {
dioxus::webview::launch(Example);
}
mod components {
use baller::Baller;
use dioxus::prelude::*;
use baller::Baller;
use dioxus_core::prelude::*;
fn example() {
let g = rsx! {
static Example: FC<()> = |ctx| {
ctx.render(rsx! {
div {
crate::components::baller::Baller {}
baller::Baller {
}
Taller {
a: "asd"
}
baller::Baller {}
baller::Baller {}
Baller {
// todo: manual props
// {...BallerProps {}}
}
div {
a: "asd",
a: "asd",
a: "asd",
a: "asd",
div {
"asdas",
"asdas",
"asdas",
"asdas",
div {},
div {
class: "asdasd"
},
}
}
}
};
}
// Elements
mod baller {
// ==============
// Components
// ==============
// Can accept any paths
crate::baller::Baller {}
baller::Baller { }
// Can take properties
Taller { a: "asd" }
// Can take optional properties
Taller { a: "asd" }
// Can pass in props directly
Taller { a: "asd" /* ..{props}*/ }
// Can take children
Taller { a: "asd", div {} }
}
})
};
mod baller {
use super::*;
pub struct BallerProps {}
pub fn Baller(ctx: Context<()>) -> VNode {
todo!()
}
}
}
#[derive(Debug, PartialEq, Props)]
pub struct TallerProps {
#[derive(Debug, PartialEq, Props)]
pub struct TallerProps {
a: &'static str,
}
}
pub fn Taller(ctx: Context<TallerProps>) -> VNode {
pub fn Taller(ctx: Context<TallerProps>) -> VNode {
let b = true;
todo!()
}
}

View File

@ -391,3 +391,101 @@ static Component: FC = |ctx, name: &str| {
}
```
## Noderefs
How do we resolve noderefs in a world of patches? Patches _must_ be serializable, so if we do something like `Option<&RefCell<Slot>>`, then that must serialize as _something_ to indicate to a remote host that access to the node itself is desired. Our `Slot` type will need to be somewhat abstract.
If we add a new patch type called "BindRef" we could do something like:
```rust
enum Patch {
//...
BindAsRef { raw_node: &RefCell<Option<Slot>> }
}
```
```rust
let node_ref = use_node_ref(&ctx);
use_effect(&ctx, || {
}, []);
div { ref: node_ref,
"hello me"
h3 {"yo dom"}
}
```
refs only work when you're native to the platform. it doesn't make sense to gain a ref when you're not native.
## In-sync or separate?
React makes refs - and protection against dom manipulation - work by modifying the real dom while diffing the virtual dom. This lets it bind real dom elements to the virtual dom elements. Dioxus currently does not do this, instead creating a list of changes for an interpreter to apply once diffing has completed.
This behavior fit dodrio well as all dom manipulations would occur batched. The original intention for this approach was to make it faster to read out of wasm and into JS. Dodrio is essentially performing the wasm job that wasm<->js for strings does. In theory, this particular optimization is not necessary.
https://github.com/fitzgen/dodrio/issues/77
This issue/pr on the dodrio repository points to a future where elements are held on to by the virtualdom.
Can we solve events, refs, and protection against 3rd party dom mutation all in one shot?
I think we can....
every node gets a globally unique ID
abstract the real dom
```rust
trait RealDom {
type Node: RealNode;
fn get_node(&self, id: u32) -> &Self::Node;
fn get_node_mut(&mut self, id: u32) -> &mut Self::Node;
fn replace_node();
fn create_text_node();
fn create_element();
fn create_element_ns();
}
trait RealNode {
fn add_listener(&mut self, event: &str);
fn set_inner_text(&mut self, text: &str);
fn set_attr(&mut self, name, value);
fn set_class(&mut self);
fn remove_attr(&mut self);
fn downcast_mut<T>(&mut self) -> Option<&mut T>;
}
impl VirtualDom<Dom: RealDom> {
fn diff<Dom: RealDom>() {
}
}
enum VNode<'bump, 'realdom, RealDom> {
VElement {
real: &RealDom::Node
}
VText {
real: &RealDom::Node
}
}
fn main() {
let mut real_dom = websys::Document();
let virtual_dom = Dioxus::VirtualDom::new();
virtual_dom.rebuild(&mut real_dom);
loop {
let event = switch! {
real_dom.events.await => event,
virtual_dom.inner_events.await => event
};
virtual_dom.apply_event(&mut real_dom, event);
}
}
```

18
notes/TODO.md Normal file
View File

@ -0,0 +1,18 @@
- [] Move the builder API onto NodeCtx
- [] Transition away from names and towards compile-time safe tags
- [] Fix diffing of fragments
- [] Properly integrate memoization to prevent safety issues with children
- [] Understand the issue with callbacks (outdated generations)
- [] Fix examples for core, web, ssr, and general
- [] Finish up documentation
- [] Polish the Recoil (Dirac?) API
- [] Find better names for things
- [] get the html macro up to parity with the rsx! macro
- [] put warnings in for iterating w/o keys
- [] finish up the crate for generating code blocks
- [] make progress on the docusarus-style crate for generating the websites
- [] build the docsite
- [] find a way to serialize the virtualdom completely
- [] ...some how deserialize (hydrate) the dom state?
- [] Implement controlled inputs for select and textarea
- [] ...somehow... noderefs....

View File

@ -50,14 +50,9 @@ impl Parse for AmbiguousElement {
false => {
let first_char = name_str.chars().next().unwrap();
if first_char.is_ascii_uppercase() {
match name_str.as_str() {
"Fragment" => input
.parse::<Fragment>()
.map(|c| AmbiguousElement::Fragment(c)),
_ => input
input
.parse::<Component>()
.map(|c| AmbiguousElement::Component(c)),
}
.map(|c| AmbiguousElement::Component(c))
} else {
let name = input.parse::<Ident>().unwrap();
Err(Error::new(

View File

@ -34,30 +34,16 @@ pub fn fc_to_builder<T: Properties>(_: FC<T>) -> T::Builder {
T::builder()
}
mod eliminate_bounds {
struct BNode<'a> {
_p: &'a (),
}
trait Broper {}
impl Broper for () {}
struct Bontext<'a, P: Broper + 'a> {
inner: &'a (),
props: P,
}
fn Bexample(c: Bontext<()>) -> BNode {
todo!()
}
struct MyBrops<'a> {
_i: &'a (),
}
impl<'a> Broper for MyBrops<'a> {}
fn Bexample2<'a>(c: Bontext<'a, MyBrops<'a>>) -> BNode {
todo!()
}
}
/// Create inline fragments
/// --
///
/// Fragments capture a series of children without rendering extra nodes.
///
///
///
pub static Fragment: FC<()> = |ctx| {
use crate::prelude::*;
ctx.render(LazyNodes::new(move |c| {
crate::nodebuilder::vfragment(c, None, ctx.children())
}))
};

View File

@ -50,7 +50,7 @@ pub(crate) mod innerlude {
/// Re-export common types for ease of development use.
/// Essential when working with the html! macro
pub mod prelude {
pub use crate::component::{fc_to_builder, Properties};
pub use crate::component::{fc_to_builder, Fragment, Properties};
use crate::nodes;
pub use crate::virtual_dom::Context;
pub use crate::virtual_dom::Scoped;