wip: making progress on diffing and hydration
This commit is contained in:
1 parent
e5c88fe3a4
commit
49856ccd68
34 files changed
+1023
-750
No files matched your search
@@ -0,0 +1,21 @@
|
||||
pub struct WebConfig {
|
||||
pub(crate) hydrate: bool,
|
||||
}
|
||||
impl Default for WebConfig {
|
||||
fn default() -> Self {
|
||||
Self { hydrate: false }
|
||||
}
|
||||
}
|
||||
impl WebConfig {
|
||||
/// Enable SSR hydration
|
||||
///
|
||||
/// This enables Dioxus to pick up work from a pre-renderd HTML file. Hydration will completely skip over any async
|
||||
/// work and suspended nodes.
|
||||
///
|
||||
/// Dioxus will load up all the elements with the `dio_el` data attribute into memory when the page is loaded.
|
||||
///
|
||||
pub fn hydrate(mut self, f: bool) -> Self {
|
||||
self.hydrate = f;
|
||||
self
|
||||
}
|
||||
}
|
||||
@@ -8,12 +8,19 @@ use fxhash::FxHashMap;
|
||||
use wasm_bindgen::{closure::Closure, JsCast};
|
||||
use web_sys::{
|
||||
window, Document, Element, Event, HtmlElement, HtmlInputElement, HtmlOptionElement, Node,
|
||||
NodeList,
|
||||
};
|
||||
|
||||
use crate::{nodeslab::NodeSlab, WebConfig};
|
||||
|
||||
pub struct WebsysDom {
|
||||
pub stack: Stack,
|
||||
nodes: Vec<Node>,
|
||||
stack: Stack,
|
||||
|
||||
/// A map from ElementID (index) to Node
|
||||
nodes: NodeSlab,
|
||||
|
||||
document: Document,
|
||||
|
||||
root: Element,
|
||||
|
||||
event_receiver: async_channel::Receiver<EventTrigger>,
|
||||
@@ -33,11 +40,8 @@ pub struct WebsysDom {
|
||||
last_node_was_text: bool,
|
||||
}
|
||||
impl WebsysDom {
|
||||
pub fn new(root: Element) -> Self {
|
||||
let document = window()
|
||||
.expect("must have access to the window")
|
||||
.document()
|
||||
.expect("must have access to the Document");
|
||||
pub fn new(root: Element, cfg: WebConfig) -> Self {
|
||||
let document = load_document();
|
||||
|
||||
let (sender, receiver) = async_channel::unbounded::<EventTrigger>();
|
||||
|
||||
@@ -48,14 +52,36 @@ impl WebsysDom {
|
||||
});
|
||||
});
|
||||
|
||||
let mut nodes = Vec::with_capacity(1000);
|
||||
let mut nodes = NodeSlab::new(2000);
|
||||
let mut listeners = FxHashMap::default();
|
||||
|
||||
// let root_id = nodes.insert(root.clone().dyn_into::<Node>().unwrap());
|
||||
// re-hydrate the page - only supports one virtualdom per page
|
||||
if cfg.hydrate {
|
||||
// Load all the elements into the arena
|
||||
let node_list: NodeList = document.query_selector_all("dio_el").unwrap();
|
||||
let len = node_list.length() as usize;
|
||||
|
||||
for x in 0..len {
|
||||
let node: Node = node_list.get(x as u32).unwrap();
|
||||
let el: &Element = node.dyn_ref::<Element>().unwrap();
|
||||
let id: String = el.get_attribute("dio_el").unwrap();
|
||||
let id = id.parse::<usize>().unwrap();
|
||||
|
||||
// this autoresizes the vector if needed
|
||||
nodes[id] = Some(node);
|
||||
}
|
||||
|
||||
// Load all the event listeners into our listener register
|
||||
}
|
||||
|
||||
let mut stack = Stack::with_capacity(10);
|
||||
let root_node = root.clone().dyn_into::<Node>().unwrap();
|
||||
stack.push(root_node);
|
||||
|
||||
Self {
|
||||
stack: Stack::with_capacity(10),
|
||||
stack,
|
||||
nodes,
|
||||
listeners: FxHashMap::default(),
|
||||
listeners,
|
||||
document,
|
||||
event_receiver: receiver,
|
||||
trigger: sender_callback,
|
||||
@@ -97,19 +123,14 @@ impl WebsysDom {
|
||||
}
|
||||
}
|
||||
fn push(&mut self, root: u64) {
|
||||
// let key = DefaultKey::from(KeyData::from_ffi(root));
|
||||
let key = root as usize;
|
||||
let domnode = self.nodes.get_mut(key);
|
||||
let domnode = &self.nodes[key];
|
||||
|
||||
let real_node: Node = match domnode {
|
||||
Some(n) => n.clone(),
|
||||
None => todo!(),
|
||||
};
|
||||
|
||||
// let domnode = domnode.unwrap().as_mut().unwrap();
|
||||
// .expect(&format!("Failed to pop know root: {:#?}", key))
|
||||
// .unwrap();
|
||||
|
||||
self.stack.push(real_node);
|
||||
}
|
||||
// drop the node off the stack
|
||||
@@ -205,7 +226,7 @@ impl WebsysDom {
|
||||
|
||||
let id = id as usize;
|
||||
self.stack.push(textnode.clone());
|
||||
*self.nodes.get_mut(id).unwrap() = textnode;
|
||||
self.nodes[id] = Some(textnode);
|
||||
}
|
||||
|
||||
fn create_element(&mut self, tag: &str, ns: Option<&'static str>, id: u64) {
|
||||
@@ -227,7 +248,7 @@ impl WebsysDom {
|
||||
let id = id as usize;
|
||||
|
||||
self.stack.push(el.clone());
|
||||
*self.nodes.get_mut(id).unwrap() = el;
|
||||
self.nodes[id] = Some(el);
|
||||
// let nid = self.node_counter.?next();
|
||||
// let nid = self.nodes.insert(el).data().as_ffi();
|
||||
// log::debug!("Called [`create_element`]: {}, {:?}", tag, nid);
|
||||
@@ -264,6 +285,9 @@ impl WebsysDom {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
el.set_attribute(&format!("dioxus-event"), &format!("{}", event))
|
||||
.unwrap();
|
||||
|
||||
// Register the callback to decode
|
||||
|
||||
if let Some(entry) = self.listeners.get_mut(event) {
|
||||
@@ -512,26 +536,6 @@ fn virtual_event_from_websys_event(event: &web_sys::Event) -> VirtualEvent {
|
||||
}
|
||||
}
|
||||
VirtualEvent::MouseEvent(MouseEvent(Rc::new(CustomMouseEvent(evt))))
|
||||
// MouseEvent(Box::new(RawMouseEvent {
|
||||
// alt_key: evt.alt_key(),
|
||||
// button: evt.button() as i32,
|
||||
// buttons: evt.buttons() as i32,
|
||||
// client_x: evt.client_x(),
|
||||
// client_y: evt.client_y(),
|
||||
// ctrl_key: evt.ctrl_key(),
|
||||
// meta_key: evt.meta_key(),
|
||||
// page_x: evt.page_x(),
|
||||
// page_y: evt.page_y(),
|
||||
// screen_x: evt.screen_x(),
|
||||
// screen_y: evt.screen_y(),
|
||||
// shift_key: evt.shift_key(),
|
||||
// get_modifier_state: GetModifierKey(Box::new(|f| {
|
||||
// // evt.get_modifier_state(f)
|
||||
// todo!("This is not yet implemented properly, sorry :(");
|
||||
// })),
|
||||
// }))
|
||||
// todo!()
|
||||
// VirtualEvent::MouseEvent()
|
||||
}
|
||||
|
||||
"pointerdown" | "pointermove" | "pointerup" | "pointercancel" | "gotpointercapture"
|
||||
@@ -645,3 +649,14 @@ fn decode_trigger(event: &web_sys::Event) -> anyhow::Result<EventTrigger> {
|
||||
dioxus_core::events::EventPriority::High,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn prepare_websys_dom() -> Element {
|
||||
load_document().get_element_by_id("dioxusroot").unwrap()
|
||||
}
|
||||
|
||||
pub fn load_document() -> Document {
|
||||
web_sys::window()
|
||||
.expect("should have access to the Window")
|
||||
.document()
|
||||
.expect("should have access to the Document")
|
||||
}
|
||||
+54
-33
@@ -2,38 +2,66 @@
|
||||
//! --------------
|
||||
//! This crate implements a renderer of the Dioxus Virtual DOM for the web browser using Websys.
|
||||
|
||||
pub use crate::cfg::WebConfig;
|
||||
use crate::dom::load_document;
|
||||
use dioxus::prelude::{Context, Properties, VNode};
|
||||
use dioxus::virtual_dom::VirtualDom;
|
||||
pub use dioxus_core as dioxus;
|
||||
use dioxus_core::error::Result;
|
||||
use dioxus_core::{events::EventTrigger, prelude::FC};
|
||||
use futures_util::{pin_mut, Stream, StreamExt};
|
||||
use fxhash::FxHashMap;
|
||||
use web_sys::{window, Document, Element, Event, Node};
|
||||
use js_sys::Iterator;
|
||||
use web_sys::{window, Document, Element, Event, Node, NodeList};
|
||||
|
||||
mod cache;
|
||||
mod new;
|
||||
mod cfg;
|
||||
mod dom;
|
||||
mod nodeslab;
|
||||
|
||||
/// Launches the VirtualDOM from the specified component function.
|
||||
///
|
||||
/// This method will block the thread with `spawn_local`
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
///
|
||||
///
|
||||
pub fn launch<F>(root: FC<()>, config: F)
|
||||
where
|
||||
F: FnOnce(()),
|
||||
F: FnOnce(WebConfig) -> WebConfig,
|
||||
{
|
||||
wasm_bindgen_futures::spawn_local(run(root))
|
||||
launch_with_props(root, (), config)
|
||||
}
|
||||
|
||||
/// Launches the VirtualDOM from the specified component function and props.
|
||||
///
|
||||
/// This method will block the thread with `spawn_local`
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
///
|
||||
pub fn launch_with_props<T, F>(root: FC<T>, root_props: T, config: F)
|
||||
where
|
||||
T: Properties + 'static,
|
||||
F: FnOnce(()),
|
||||
F: FnOnce(WebConfig) -> WebConfig,
|
||||
{
|
||||
wasm_bindgen_futures::spawn_local(run_with_props(root, root_props))
|
||||
let config = config(WebConfig::default());
|
||||
let fut = run_with_props(root, root_props, config);
|
||||
|
||||
wasm_bindgen_futures::spawn_local(async {
|
||||
match fut.await {
|
||||
Ok(_) => log::error!("Your app completed running... somehow?"),
|
||||
Err(e) => log::error!("Your app crashed! {}", e),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// This method is the primary entrypoint for Websys Dioxus apps. Will panic if an error occurs while rendering.
|
||||
/// See DioxusErrors for more information on how these errors could occour.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```ignore
|
||||
/// fn main() {
|
||||
/// wasm_bindgen_futures::spawn_local(WebsysRenderer::start(Example));
|
||||
@@ -42,29 +70,19 @@ where
|
||||
///
|
||||
/// Run the app to completion, panicing if any error occurs while rendering.
|
||||
/// Pairs well with the wasm_bindgen async handler
|
||||
pub async fn run(root: FC<()>) {
|
||||
run_with_props(root, ()).await;
|
||||
}
|
||||
|
||||
pub async fn run_with_props<T: Properties + 'static>(root: FC<T>, root_props: T) {
|
||||
pub async fn run_with_props<T: Properties + 'static>(
|
||||
root: FC<T>,
|
||||
root_props: T,
|
||||
cfg: WebConfig,
|
||||
) -> Result<()> {
|
||||
let dom = VirtualDom::new_with_props(root, root_props);
|
||||
event_loop(dom).await.expect("Event loop failed");
|
||||
}
|
||||
|
||||
pub async fn event_loop(mut internal_dom: VirtualDom) -> dioxus_core::error::Result<()> {
|
||||
use wasm_bindgen::JsCast;
|
||||
let root_el = load_document().get_element_by_id("dioxus_root").unwrap();
|
||||
let mut websys_dom = dom::WebsysDom::new(root_el, cfg);
|
||||
|
||||
let root = prepare_websys_dom();
|
||||
let root_node = root.clone().dyn_into::<Node>().unwrap();
|
||||
|
||||
let mut websys_dom = crate::new::WebsysDom::new(root.clone());
|
||||
|
||||
websys_dom.stack.push(root_node.clone());
|
||||
websys_dom.stack.push(root_node);
|
||||
|
||||
let mut edits = Vec::new();
|
||||
internal_dom.rebuild(&mut websys_dom, &mut edits)?;
|
||||
websys_dom.process_edits(&mut edits);
|
||||
// let mut edits = Vec::new();
|
||||
// internal_dom.rebuild(&mut websys_dom, &mut edits)?;
|
||||
// websys_dom.process_edits(&mut edits);
|
||||
|
||||
log::info!("Going into event loop");
|
||||
loop {
|
||||
@@ -105,11 +123,14 @@ pub async fn event_loop(mut internal_dom: VirtualDom) -> dioxus_core::error::Res
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn prepare_websys_dom() -> Element {
|
||||
web_sys::window()
|
||||
.expect("should have access to the Window")
|
||||
.document()
|
||||
.expect("should have access to the Document")
|
||||
.get_element_by_id("dioxusroot")
|
||||
.unwrap()
|
||||
fn iter_node_list() {}
|
||||
|
||||
// struct NodeListIter {
|
||||
// node_list: NodeList,
|
||||
// }
|
||||
// impl Iterator for NodeListIter {}
|
||||
|
||||
struct HydrationNode {
|
||||
id: usize,
|
||||
node: Node,
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
use std::ops::{Index, IndexMut};
|
||||
|
||||
use web_sys::Node;
|
||||
|
||||
pub struct NodeSlab {
|
||||
nodes: Vec<Option<Node>>,
|
||||
}
|
||||
|
||||
impl NodeSlab {
|
||||
pub fn new(capacity: usize) -> NodeSlab {
|
||||
NodeSlab {
|
||||
nodes: Vec::with_capacity(capacity),
|
||||
}
|
||||
}
|
||||
|
||||
fn insert_and_extend(&mut self, node: Node, id: usize) {
|
||||
if id > self.nodes.len() * 3 {
|
||||
panic!("Trying to insert an element way too far out of bounds");
|
||||
}
|
||||
|
||||
if id < self.nodes.len() {}
|
||||
}
|
||||
}
|
||||
impl Index<usize> for NodeSlab {
|
||||
type Output = Option<Node>;
|
||||
fn index(&self, index: usize) -> &Self::Output {
|
||||
&self.nodes[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<usize> for NodeSlab {
|
||||
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||
if index >= self.nodes.len() * 3 {
|
||||
panic!("Trying to mutate an element way too far out of bounds");
|
||||
}
|
||||
if index > self.nodes.len() {
|
||||
self.nodes.resize_with(index, || None);
|
||||
}
|
||||
&mut self.nodes[index]
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user