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

+68 -35
View File
@@ -1,6 +1,5 @@
use crate::{GlobalAttributes, SvgAttributes};
use dioxus_core::*;
use std::fmt::Arguments;
macro_rules! builder_constructors {
(
@@ -28,10 +27,12 @@ macro_rules! builder_constructors {
impl $name {
$(
$(#[$attr_method])*
pub fn $fil<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
cx.attr(stringify!($fil), val, None, false)
}
#[allow(non_upper_case_globals)]
pub const $fil: AttributeDiscription = AttributeDiscription{
name: stringify!($fil),
namespace: None,
volatile: false
};
)*
}
)*
@@ -57,9 +58,13 @@ macro_rules! builder_constructors {
impl $name {
$(
pub fn $fil<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
cx.attr(stringify!($fil), val, Some(stringify!($namespace)), false)
}
#[allow(non_upper_case_globals)]
pub const $fil: AttributeDiscription = AttributeDiscription{
name: stringify!($fil),
namespace: Some(stringify!($namespace)),
volatile: false
};
)*
}
)*
@@ -1061,13 +1066,19 @@ impl input {
/// - `time`
/// - `url`
/// - `week`
pub fn r#type<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
cx.attr("type", val, None, false)
}
#[allow(non_upper_case_globals)]
pub const r#type: AttributeDiscription = AttributeDiscription {
name: "type",
namespace: None,
volatile: false,
};
pub fn value<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
cx.attr("value", val, None, true)
}
#[allow(non_upper_case_globals)]
pub const value: AttributeDiscription = AttributeDiscription {
name: "value",
namespace: None,
volatile: true,
};
}
/*
@@ -1077,41 +1088,63 @@ volatile attributes
impl script {
// r#async: Bool,
// r#type: String, // TODO could be an enum
pub fn r#type<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
cx.attr("type", val, None, false)
}
pub fn r#script<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
cx.attr("script", val, None, false)
}
#[allow(non_upper_case_globals)]
pub const r#type: AttributeDiscription = AttributeDiscription {
name: "type",
namespace: None,
volatile: false,
};
#[allow(non_upper_case_globals)]
pub const r#script: AttributeDiscription = AttributeDiscription {
name: "script",
namespace: None,
volatile: false,
};
}
impl button {
pub fn r#type<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
cx.attr("type", val, None, false)
}
#[allow(non_upper_case_globals)]
pub const r#type: AttributeDiscription = AttributeDiscription {
name: "type",
namespace: None,
volatile: false,
};
}
impl select {
pub fn value<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
cx.attr("value", val, None, true)
}
#[allow(non_upper_case_globals)]
pub const value: AttributeDiscription = AttributeDiscription {
name: "value",
namespace: None,
volatile: true,
};
}
impl option {
pub fn selected<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
cx.attr("selected", val, None, true)
}
#[allow(non_upper_case_globals)]
pub const selected: AttributeDiscription = AttributeDiscription {
name: "selected",
namespace: None,
volatile: true,
};
}
impl textarea {
pub fn value<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
cx.attr("value", val, None, true)
}
#[allow(non_upper_case_globals)]
pub const value: AttributeDiscription = AttributeDiscription {
name: "value",
namespace: None,
volatile: true,
};
}
impl label {
pub fn r#for<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
cx.attr("for", val, None, false)
}
#[allow(non_upper_case_globals)]
pub const r#for: AttributeDiscription = AttributeDiscription {
name: "for",
namespace: None,
volatile: false,
};
}
builder_constructors! {
+30 -20
View File
@@ -1,5 +1,4 @@
use dioxus_core::*;
use std::fmt::Arguments;
macro_rules! no_namespace_trait_methods {
(
@@ -9,10 +8,12 @@ macro_rules! no_namespace_trait_methods {
)*
) => {
$(
$(#[$attr])*
fn $name<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
cx.attr(stringify!($name), val, None, false)
}
#[allow(non_upper_case_globals)]
const $name: AttributeDiscription = AttributeDiscription{
name: stringify!($name),
namespace: None,
volatile: false
};
)*
};
}
@@ -24,11 +25,12 @@ macro_rules! style_trait_methods {
)*
) => {
$(
#[inline]
$(#[$attr])*
fn $name<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
cx.attr($lit, val, Some("style"), false)
}
#[allow(non_upper_case_globals)]
const $name: AttributeDiscription = AttributeDiscription{
name: $lit,
namespace: Some("style"),
volatile: false
};
)*
};
}
@@ -40,10 +42,12 @@ macro_rules! aria_trait_methods {
)*
) => {
$(
$(#[$attr])*
fn $name<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
cx.attr($lit, val, None, false)
}
#[allow(non_upper_case_globals)]
const $name: AttributeDiscription = AttributeDiscription{
name: $lit,
namespace: None,
volatile: false
};
)*
};
}
@@ -53,9 +57,12 @@ pub trait GlobalAttributes {
///
/// For more information, see the MDN docs:
/// <https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault>
fn prevent_default<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
cx.attr("dioxus-prevent-default", val, None, false)
}
#[allow(non_upper_case_globals)]
const prevent_default: AttributeDiscription = AttributeDiscription {
name: "dioxus-prevent-default",
namespace: None,
volatile: false,
};
no_namespace_trait_methods! {
accesskey;
@@ -597,9 +604,12 @@ pub trait SvgAttributes {
///
/// For more information, see the MDN docs:
/// <https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault>
fn prevent_default<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
cx.attr("dioxus-prevent-default", val, None, false)
}
#[allow(non_upper_case_globals)]
const prevent_default: AttributeDiscription = AttributeDiscription {
name: "dioxus-prevent-default",
namespace: None,
volatile: false,
};
aria_trait_methods! {
accent_height: "accent-height",
accumulate: "accumulate",