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
@@ -3,7 +3,7 @@ use std::{
|
||||
fmt::{Result, Write},
|
||||
};
|
||||
|
||||
use dioxus_rsx::{BodyNode, ElementAttr, ElementAttrNamed};
|
||||
use dioxus_rsx::{BodyNode, ElementAttr, ElementAttrNamed, IfmtInput};
|
||||
use proc_macro2::{LineColumn, Span};
|
||||
use syn::{spanned::Spanned, Expr};
|
||||
|
||||
@@ -72,8 +72,8 @@ impl Buffer {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_text(&mut self, text: &syn::LitStr) -> Result {
|
||||
write!(self.buf, "\"{}\"", text.value())
|
||||
pub fn write_text(&mut self, text: &IfmtInput) -> Result {
|
||||
write!(self.buf, "\"{}\"", text.source.as_ref().unwrap().value())
|
||||
}
|
||||
|
||||
pub fn consume(self) -> Option<String> {
|
||||
@@ -155,13 +155,13 @@ impl Buffer {
|
||||
|
||||
total += match &attr.attr {
|
||||
ElementAttr::AttrText { value, name } => {
|
||||
value.value().len() + name.span().line_length() + 3
|
||||
value.source.as_ref().unwrap().value().len() + name.span().line_length() + 3
|
||||
}
|
||||
ElementAttr::AttrExpression { name, value } => {
|
||||
value.span().line_length() + name.span().line_length() + 3
|
||||
}
|
||||
ElementAttr::CustomAttrText { value, name } => {
|
||||
value.value().len() + name.value().len() + 3
|
||||
value.source.as_ref().unwrap().value().len() + name.value().len() + 3
|
||||
}
|
||||
ElementAttr::CustomAttrExpression { name, value } => {
|
||||
name.value().len() + value.span().line_length() + 3
|
||||
|
||||
@@ -167,7 +167,12 @@ impl Buffer {
|
||||
write!(self.buf, "{}: {}", name, out)?;
|
||||
}
|
||||
ContentField::Formatted(s) => {
|
||||
write!(self.buf, "{}: \"{}\"", name, s.value())?;
|
||||
write!(
|
||||
self.buf,
|
||||
"{}: \"{}\"",
|
||||
name,
|
||||
s.source.as_ref().unwrap().value()
|
||||
)?;
|
||||
}
|
||||
ContentField::OnHandlerRaw(exp) => {
|
||||
let out = prettyplease::unparse_expr(exp);
|
||||
@@ -209,7 +214,7 @@ impl Buffer {
|
||||
let attr_len = fields
|
||||
.iter()
|
||||
.map(|field| match &field.content {
|
||||
ContentField::Formatted(s) => s.value().len() ,
|
||||
ContentField::Formatted(s) => s.source.as_ref().unwrap().value().len() ,
|
||||
ContentField::OnHandlerRaw(exp) | ContentField::ManExpr(exp) => {
|
||||
let formatted = prettyplease::unparse_expr(exp);
|
||||
let len = if formatted.contains('\n') {
|
||||
|
||||
@@ -132,7 +132,7 @@ impl Buffer {
|
||||
fn write_attributes(
|
||||
&mut self,
|
||||
attributes: &[ElementAttrNamed],
|
||||
key: &Option<syn::LitStr>,
|
||||
key: &Option<IfmtInput>,
|
||||
sameline: bool,
|
||||
) -> Result {
|
||||
let mut attr_iter = attributes.iter().peekable();
|
||||
@@ -141,7 +141,11 @@ impl Buffer {
|
||||
if !sameline {
|
||||
self.indented_tabbed_line()?;
|
||||
}
|
||||
write!(self.buf, "key: \"{}\"", key.value())?;
|
||||
write!(
|
||||
self.buf,
|
||||
"key: \"{}\"",
|
||||
key.source.as_ref().unwrap().value()
|
||||
)?;
|
||||
if !attributes.is_empty() {
|
||||
write!(self.buf, ",")?;
|
||||
if sameline {
|
||||
@@ -178,7 +182,11 @@ impl Buffer {
|
||||
fn write_attribute(&mut self, attr: &ElementAttrNamed) -> Result {
|
||||
match &attr.attr {
|
||||
ElementAttr::AttrText { name, value } => {
|
||||
write!(self.buf, "{name}: \"{value}\"", value = value.value())?;
|
||||
write!(
|
||||
self.buf,
|
||||
"{name}: \"{value}\"",
|
||||
value = value.source.as_ref().unwrap().value()
|
||||
)?;
|
||||
}
|
||||
ElementAttr::AttrExpression { name, value } => {
|
||||
let out = prettyplease::unparse_expr(value);
|
||||
@@ -190,7 +198,7 @@ impl Buffer {
|
||||
self.buf,
|
||||
"\"{name}\": \"{value}\"",
|
||||
name = name.value(),
|
||||
value = value.value()
|
||||
value = value.source.as_ref().unwrap().value()
|
||||
)?;
|
||||
}
|
||||
|
||||
@@ -272,7 +280,7 @@ impl Buffer {
|
||||
}
|
||||
|
||||
match children {
|
||||
[BodyNode::Text(ref text)] => Some(text.value().len()),
|
||||
[BodyNode::Text(ref text)] => Some(text.source.as_ref().unwrap().value().len()),
|
||||
[BodyNode::Component(ref comp)] => {
|
||||
let attr_len = self.field_len(&comp.fields, &comp.manual_props);
|
||||
|
||||
|
||||
Reference in new issue
Block a user