Subtree memorization / reactive templates (#488)
This commit adds subtree memoization to Dioxus. Subtree memoization is basically a compile-time step that drastically reduces the amount of work the diffing engine needs to do at runtime by extracting non-changing nodes out into a static "template." Templates are then understood by the various renderers in the ecosystem as a faster way of rendering the same items. For example, in the web, templates are simply a set of DOM Nodes created once and then cloned later. This is the same pattern frameworks like Lithtml and SolidJS use to achieve near-perfect performance. Subtree memoization adds an additional level of complexity to Dioxus. The RSX macro needs to be much smarter to identify changing/nonchanging nodes and generate a mapping between the Template and its runtime counterparts. This commit represents a working starter point for this work, adding support for templates for the web, desktop, liveview, ssr, and native-core renderers. In the future we will try to shrink code generation, generally improve performance, and simplify our implementation.
This commit is contained in:
1 parent
b32fd2d2cd
commit
047ed1e553
80 files changed
+6774
-2552
No files matched your search
@@ -51,7 +51,7 @@ impl DesktopController {
|
||||
dom.base_scope().provide_context(window_context);
|
||||
|
||||
// allow other proccesses to send the new rsx text to the @dioxusin ipc channel and recieve erros on the @dioxusout channel
|
||||
#[cfg(feature = "hot-reload")]
|
||||
#[cfg(any(feature = "hot-reload", debug_assertions))]
|
||||
crate::hot_reload::init(&dom);
|
||||
|
||||
let edits = dom.rebuild();
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
use std::any::Any;
|
||||
use std::sync::Arc;
|
||||
|
||||
use dioxus_core::{ElementId, EventPriority, UserEvent};
|
||||
use dioxus_core::{EventPriority, GlobalNodeId, UserEvent};
|
||||
use dioxus_html::event_bubbles;
|
||||
use dioxus_html::on::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -37,7 +37,7 @@ pub(crate) fn parse_ipc_message(payload: &str) -> Option<IpcMessage> {
|
||||
#[derive(Deserialize, Serialize)]
|
||||
struct ImEvent {
|
||||
event: String,
|
||||
mounted_dom_id: u64,
|
||||
mounted_dom_id: GlobalNodeId,
|
||||
contents: serde_json::Value,
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ pub fn trigger_from_serialized(val: serde_json::Value) -> UserEvent {
|
||||
contents,
|
||||
} = serde_json::from_value(val).unwrap();
|
||||
|
||||
let mounted_dom_id = Some(ElementId(mounted_dom_id as usize));
|
||||
let mounted_dom_id = Some(mounted_dom_id);
|
||||
let name = event_name_from_type(&event);
|
||||
|
||||
let event = make_synthetic_event(&event, contents);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use dioxus_core::VirtualDom;
|
||||
use dioxus_rsx_interpreter::{error::Error, ErrorHandler, SetManyRsxMessage, RSX_CONTEXT};
|
||||
use dioxus_core::{SchedulerMsg, SetTemplateMsg, VirtualDom};
|
||||
use interprocess::local_socket::{LocalSocketListener, LocalSocketStream};
|
||||
use std::io::{BufRead, BufReader, Write};
|
||||
use std::io::{BufRead, BufReader};
|
||||
use std::time::Duration;
|
||||
use std::{sync::Arc, sync::Mutex};
|
||||
|
||||
@@ -15,29 +14,6 @@ pub(crate) fn init(dom: &VirtualDom) {
|
||||
let latest_in_connection: Arc<Mutex<Option<BufReader<LocalSocketStream>>>> =
|
||||
Arc::new(Mutex::new(None));
|
||||
let latest_in_connection_handle = latest_in_connection.clone();
|
||||
let latest_out_connection: Arc<Mutex<Option<BufReader<LocalSocketStream>>>> =
|
||||
Arc::new(Mutex::new(None));
|
||||
let latest_out_connection_handle = latest_out_connection.clone();
|
||||
|
||||
struct DesktopErrorHandler {
|
||||
latest_connection: Arc<Mutex<Option<BufReader<LocalSocketStream>>>>,
|
||||
}
|
||||
impl ErrorHandler for DesktopErrorHandler {
|
||||
fn handle_error(&self, err: Error) {
|
||||
if let Some(conn) = &mut *self.latest_connection.lock().unwrap() {
|
||||
conn.get_mut()
|
||||
.write_all((serde_json::to_string(&err).unwrap() + "\n").as_bytes())
|
||||
.unwrap();
|
||||
} else {
|
||||
panic!("{}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RSX_CONTEXT.set_error_handler(DesktopErrorHandler {
|
||||
latest_connection: latest_out_connection_handle,
|
||||
});
|
||||
RSX_CONTEXT.provide_scheduler_channel(dom.get_scheduler_channel());
|
||||
|
||||
// connect to processes for incoming data
|
||||
std::thread::spawn(move || {
|
||||
@@ -48,14 +24,7 @@ pub(crate) fn init(dom: &VirtualDom) {
|
||||
}
|
||||
});
|
||||
|
||||
// connect to processes for outgoing errors
|
||||
std::thread::spawn(move || {
|
||||
if let Ok(listener) = LocalSocketListener::bind("@dioxusout") {
|
||||
for conn in listener.incoming().filter_map(handle_error) {
|
||||
*latest_out_connection.lock().unwrap() = Some(BufReader::new(conn));
|
||||
}
|
||||
}
|
||||
});
|
||||
let mut channel = dom.get_scheduler_channel();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
loop {
|
||||
@@ -63,8 +32,10 @@ pub(crate) fn init(dom: &VirtualDom) {
|
||||
let mut buf = String::new();
|
||||
match conn.read_line(&mut buf) {
|
||||
Ok(_) => {
|
||||
let msgs: SetManyRsxMessage = serde_json::from_str(&buf).unwrap();
|
||||
RSX_CONTEXT.extend(msgs);
|
||||
let msg: SetTemplateMsg = serde_json::from_str(&buf).unwrap();
|
||||
channel
|
||||
.start_send(SchedulerMsg::SetTemplate(Box::new(msg)))
|
||||
.unwrap();
|
||||
}
|
||||
Err(err) => {
|
||||
if err.kind() != std::io::ErrorKind::WouldBlock {
|
||||
|
||||
@@ -8,7 +8,7 @@ mod controller;
|
||||
mod desktop_context;
|
||||
mod escape;
|
||||
mod events;
|
||||
#[cfg(feature = "hot-reload")]
|
||||
#[cfg(any(feature = "hot-reload", debug_assertions))]
|
||||
mod hot_reload;
|
||||
mod protocol;
|
||||
|
||||
|
||||
Reference in new issue
Block a user