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:
Demonthos authored and GitHub committed 2022-09-30 12:03:06 -07:00
1 parent b32fd2d2cd
commit 047ed1e553
80 files changed
+6774 -2552

No files matched your search

+12 -31
View File
@@ -34,40 +34,21 @@ pub fn derive_typed_builder(input: proc_macro::TokenStream) -> proc_macro::Token
/// ```
#[proc_macro]
pub fn rsx(s: TokenStream) -> TokenStream {
#[cfg(feature = "hot-reload")]
let rsx_text = s.to_string();
match syn::parse::<rsx::CallBody>(s) {
Err(err) => err.to_compile_error().into(),
Ok(body) => body.to_token_stream().into(),
}
}
/// A version of the rsx! macro that does not use templates. Used for testing diffing
#[proc_macro]
pub fn rsx_without_templates(s: TokenStream) -> TokenStream {
match syn::parse::<rsx::CallBody>(s) {
Err(err) => err.to_compile_error().into(),
Ok(body) => {
#[cfg(feature = "hot-reload")]
{
use dioxus_rsx_interpreter::captuered_context::CapturedContextBuilder;
match CapturedContextBuilder::from_call_body(body) {
Ok(captured) => {
let lazy = quote::quote! {
LazyNodes::new(move |__cx|{
let code_location = get_line_num!();
let captured = #captured;
let text = #rsx_text;
resolve_scope(code_location, text, captured, __cx)
})
};
if let Some(cx) = captured.custom_context {
quote::quote! {
#cx.render(#lazy)
}
.into()
} else {
lazy.into()
}
}
Err(err) => err.into_compile_error().into(),
}
}
#[cfg(not(feature = "hot-reload"))]
body.to_token_stream().into()
let mut tokens = proc_macro2::TokenStream::new();
body.to_tokens_without_template(&mut tokens);
tokens.into()
}
}
}