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 -17
View File
@@ -10,10 +10,10 @@ use syn::{
// =======================================
// Parse the VNode::Element type
// =======================================
#[derive(PartialEq, Eq)]
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
pub struct Element {
pub name: Ident,
pub key: Option<LitStr>,
pub key: Option<IfmtInput>,
pub attributes: Vec<ElementAttrNamed>,
pub children: Vec<BodyNode>,
pub _is_static: bool,
@@ -46,7 +46,7 @@ impl Parse for Element {
content.parse::<Token![:]>()?;
if content.peek(LitStr) && content.peek2(Token![,]) {
let value = content.parse::<LitStr>()?;
let value = content.parse()?;
attributes.push(ElementAttrNamed {
el_name: el_name.clone(),
attr: ElementAttr::CustomAttrText { name, value },
@@ -164,7 +164,7 @@ impl ToTokens for Element {
let children = &self.children;
let key = match &self.key {
Some(ty) => quote! { Some(format_args_f!(#ty)) },
Some(ty) => quote! { Some(#ty) },
None => quote! { None },
};
@@ -190,16 +190,16 @@ impl ToTokens for Element {
}
}
#[derive(PartialEq, Eq)]
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
pub enum ElementAttr {
/// attribute: "valuee {}"
AttrText { name: Ident, value: LitStr },
AttrText { name: Ident, value: IfmtInput },
/// attribute: true,
AttrExpression { name: Ident, value: Expr },
/// "attribute": "value {}"
CustomAttrText { name: LitStr, value: LitStr },
CustomAttrText { name: LitStr, value: IfmtInput },
/// "attribute": true,
CustomAttrExpression { name: LitStr, value: Expr },
@@ -231,7 +231,7 @@ impl ElementAttr {
}
}
#[derive(PartialEq, Eq)]
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
pub struct ElementAttrNamed {
pub el_name: Ident,
pub attr: ElementAttr,
@@ -244,29 +244,24 @@ impl ToTokens for ElementAttrNamed {
tokens.append_all(match attr {
ElementAttr::AttrText { name, value } => {
quote! {
dioxus_elements::#el_name.#name(__cx, format_args_f!(#value))
__cx.attr_disciption( dioxus_elements::#el_name::#name, #value)
}
}
ElementAttr::AttrExpression { name, value } => {
quote! {
dioxus_elements::#el_name.#name(__cx, #value)
__cx.attr_disciption( dioxus_elements::#el_name::#name, #value)
}
}
ElementAttr::CustomAttrText { name, value } => {
quote! {
__cx.attr( #name, format_args_f!(#value), None, false )
__cx.attr( #name, #value, None, false )
}
}
ElementAttr::CustomAttrExpression { name, value } => {
quote! {
__cx.attr( #name, format_args_f!(#value), None, false )
__cx.attr( #name, #value, None, false )
}
}
// ElementAttr::EventClosure { name, closure } => {
// quote! {
// dioxus_elements::on::#name(__cx, #closure)
// }
// }
ElementAttr::EventTokens { name, tokens } => {
quote! {
dioxus_elements::on::#name(__cx, #tokens)