1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/79236386")]
#![doc(html_favicon_url = "https://avatars.githubusercontent.com/u/79236386")]
#![deny(missing_docs)]
//! Dioxus WebSys
//!
//! ## 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
//! - controlled components
//! - 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.
use std::rc::Rc;
pub use crate::cfg::Config;
#[cfg(feature = "file_engine")]
pub use crate::file_engine::WebFileEngineExt;
use dioxus_core::VirtualDom;
use futures_util::{pin_mut, select, FutureExt, StreamExt};
mod cfg;
mod dom;
mod event;
pub mod launch;
mod mutations;
pub use event::*;
#[cfg(feature = "eval")]
mod eval;
#[cfg(feature = "file_engine")]
mod file_engine;
#[cfg(all(feature = "hot_reload", debug_assertions))]
mod hot_reload;
#[cfg(feature = "hydrate")]
mod rehydrate;
/// Runs the app as a future that can be scheduled around the main thread.
///
/// Polls futures internal to the VirtualDOM, hence the async nature of this function.
///
/// # Example
///
/// ```ignore, rust
/// let app_fut = dioxus_web::run_with_props(App, RootProps { name: String::from("foo") });
/// wasm_bindgen_futures::spawn_local(app_fut);
/// ```
pub async fn run(virtual_dom: VirtualDom, web_config: Config) {
tracing::info!("Starting up");
let mut dom = virtual_dom;
#[cfg(feature = "eval")]
dom.in_runtime(eval::init_eval);
#[cfg(feature = "panic_hook")]
if web_config.default_panic_hook {
console_error_panic_hook::set_once();
}
#[cfg(all(feature = "hot_reload", debug_assertions))]
let mut hotreload_rx = hot_reload::init();
let (tx, mut rx) = futures_channel::mpsc::unbounded();
let should_hydrate = web_config.hydrate;
let mut websys_dom = dom::WebsysDom::new(web_config, tx);
tracing::info!("rebuilding app");
if should_hydrate {
#[cfg(feature = "hydrate")]
{
dom.rebuild(&mut crate::rehydrate::OnlyWriteTemplates(&mut websys_dom));
if let Err(err) = websys_dom.rehydrate(&dom) {
tracing::error!("Rehydration failed. {:?}", err);
tracing::error!("Rebuild DOM into element from scratch");
websys_dom.root.set_text_content(None);
dom.rebuild(&mut websys_dom);
websys_dom.flush_edits();
}
}
} else {
dom.rebuild(&mut websys_dom);
websys_dom.flush_edits();
}
// the mutations come back with nothing - we need to actually mount them
websys_dom.mount();
loop {
// if virtual dom has nothing, wait for it to have something before requesting idle time
// if there is work then this future resolves immediately.
let (mut res, template) = {
let work = dom.wait_for_work().fuse();
pin_mut!(work);
let mut rx_next = rx.select_next_some();
#[cfg(all(feature = "hot_reload", debug_assertions))]
{
let mut hot_reload_next = hotreload_rx.select_next_some();
select! {
_ = work => (None, None),
new_template = hot_reload_next => (None, Some(new_template)),
evt = rx_next => (Some(evt), None),
}
}
#[cfg(not(all(feature = "hot_reload", debug_assertions)))]
select! {
_ = work => (None, None),
evt = rx_next => (Some(evt), None),
}
};
if let Some(template) = template {
dom.replace_template(template);
}
// Dequeue all of the events from the channel in send order
// todo: we should re-order these if possible
while let Some(evt) = res {
dom.handle_event(
evt.name.as_str(),
Rc::new(evt.data),
evt.element,
evt.bubbles,
);
res = rx.try_next().transpose().unwrap().ok();
}
// Todo: This is currently disabled because it has a negative impact on response times for events but it could be re-enabled for tasks
// Jank free rendering
//
// 1. wait for the browser to give us "idle" time
// 2. During idle time, diff the dom
// 3. Stop diffing if the deadline is exceded
// 4. Wait for the animation frame to patch the dom
// wait for the mainthread to schedule us in
// let deadline = work_loop.wait_for_idle_time().await;
// run the virtualdom work phase until the frame deadline is reached
dom.render_immediate(&mut websys_dom);
// wait for the animation frame to fire so we can apply our changes
// work_loop.wait_for_raf().await;
websys_dom.flush_edits();
}
}