Merge pull request #25 from jkelleyrtp/jk/ssr_lazy
wip: clean up and add lazy renderer
This commit is contained in:
1 file changed
+90
-58
+90
-58
@@ -13,32 +13,36 @@
|
|||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
|
|
||||||
use dioxus_core::exports::bumpalo;
|
use dioxus_core::exports::bumpalo;
|
||||||
|
use dioxus_core::exports::bumpalo::Bump;
|
||||||
use dioxus_core::nodes::IntoVNode;
|
use dioxus_core::nodes::IntoVNode;
|
||||||
use dioxus_core::*;
|
use dioxus_core::*;
|
||||||
|
|
||||||
macro_rules! render_lazy {
|
/// A memory pool for rendering
|
||||||
($f:expr) => {
|
|
||||||
$crate::SsrRenderer::new().render_lazy($f)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct SsrRenderer {
|
pub struct SsrRenderer {
|
||||||
inner: bumpalo::Bump,
|
inner: bumpalo::Bump,
|
||||||
}
|
cfg: SsrConfig,
|
||||||
impl Default for SsrRenderer {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
inner: bumpalo::Bump::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SsrRenderer {
|
impl SsrRenderer {
|
||||||
pub fn new() -> Self {
|
pub fn new(cfg: impl FnOnce(SsrConfig) -> SsrConfig) -> Self {
|
||||||
SsrRenderer::default()
|
Self {
|
||||||
|
cfg: cfg(SsrConfig::default()),
|
||||||
|
inner: bumpalo::Bump::new(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_lazy<'a, F: FnOnce(NodeFactory<'a>) -> VNode<'a>>(
|
pub fn render_lazy<'a, F: FnOnce(NodeFactory<'a>) -> VNode<'a>>(
|
||||||
|
&'a mut self,
|
||||||
|
f: LazyNodes<'a, F>,
|
||||||
|
) -> String {
|
||||||
|
let bump = &mut self.inner as *mut _;
|
||||||
|
let s = self.render_inner(f);
|
||||||
|
// reuse the bump's memory
|
||||||
|
unsafe { (&mut *bump as &mut bumpalo::Bump).reset() };
|
||||||
|
s
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_inner<'a, F: FnOnce(NodeFactory<'a>) -> VNode<'a>>(
|
||||||
&'a self,
|
&'a self,
|
||||||
f: LazyNodes<'a, F>,
|
f: LazyNodes<'a, F>,
|
||||||
) -> String {
|
) -> String {
|
||||||
@@ -47,7 +51,7 @@ impl SsrRenderer {
|
|||||||
format!(
|
format!(
|
||||||
"{:}",
|
"{:}",
|
||||||
TextRenderer {
|
TextRenderer {
|
||||||
cfg: SsrConfig::default(),
|
cfg: self.cfg.clone(),
|
||||||
root: &root,
|
root: &root,
|
||||||
vdom: None
|
vdom: None
|
||||||
}
|
}
|
||||||
@@ -55,7 +59,34 @@ impl SsrRenderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_vnode(vnode: &VNode, string: &mut String) {}
|
pub fn render_lazy<'a, F: FnOnce(NodeFactory<'a>) -> VNode<'a>>(f: LazyNodes<'a, F>) -> String {
|
||||||
|
let bump = bumpalo::Bump::new();
|
||||||
|
let borrowed = ≎
|
||||||
|
|
||||||
|
// Safety
|
||||||
|
//
|
||||||
|
// The lifetimes bounds on LazyNodes are really complicated - they need to support the nesting restrictions in
|
||||||
|
// regular component usage. The <'a> lifetime is used to enforce that all calls of IntoVnode use the same allocator.
|
||||||
|
//
|
||||||
|
// When LazyNodes are provided, they are FnOnce, but do not come with a allocator selected to borrow from. The <'a>
|
||||||
|
// lifetime is therefore longer than the lifetime of the allocator which doesn't exist yet.
|
||||||
|
//
|
||||||
|
// Therefore, we cast our local bump alloactor into right lifetime. This is okay because our usage of the bump arena
|
||||||
|
// is *definitely* shorter than the <'a> lifetime, and we return owned data - not borrowed data.
|
||||||
|
// Therefore, we know that no references are leaking.
|
||||||
|
let _b: &'a Bump = unsafe { std::mem::transmute(borrowed) };
|
||||||
|
|
||||||
|
let root = f.into_vnode(NodeFactory::new(_b));
|
||||||
|
|
||||||
|
format!(
|
||||||
|
"{:}",
|
||||||
|
TextRenderer {
|
||||||
|
cfg: SsrConfig::default(),
|
||||||
|
root: &root,
|
||||||
|
vdom: None
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn render_vdom(dom: &VirtualDom, cfg: impl FnOnce(SsrConfig) -> SsrConfig) -> String {
|
pub fn render_vdom(dom: &VirtualDom, cfg: impl FnOnce(SsrConfig) -> SsrConfig) -> String {
|
||||||
format!(
|
format!(
|
||||||
@@ -124,7 +155,7 @@ impl<'a> TextRenderer<'a, '_> {
|
|||||||
}
|
}
|
||||||
write!(f, "{}", text.text)?
|
write!(f, "{}", text.text)?
|
||||||
}
|
}
|
||||||
VNode::Anchor(anchor) => {
|
VNode::Anchor(_anchor) => {
|
||||||
//
|
//
|
||||||
if self.cfg.indent {
|
if self.cfg.indent {
|
||||||
for _ in 0..il {
|
for _ in 0..il {
|
||||||
@@ -170,19 +201,16 @@ impl<'a> TextRenderer<'a, '_> {
|
|||||||
//
|
//
|
||||||
// when the page is loaded, the `querySelectorAll` will be used to collect all the nodes, and then add
|
// when the page is loaded, the `querySelectorAll` will be used to collect all the nodes, and then add
|
||||||
// them interpreter's stack
|
// them interpreter's stack
|
||||||
match (self.cfg.pre_render, node.try_mounted_id()) {
|
if let (true, Some(id)) = (self.cfg.pre_render, node.try_mounted_id()) {
|
||||||
(true, Some(id)) => {
|
write!(f, " dio_el=\"{}\"", id)?;
|
||||||
write!(f, " dio_el=\"{}\"", id)?;
|
|
||||||
//
|
for _listener in el.listeners {
|
||||||
for listener in el.listeners {
|
// todo: write the listeners
|
||||||
// write the listeners
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.cfg.newline {
|
match self.cfg.newline {
|
||||||
true => write!(f, ">\n")?,
|
true => writeln!(f, ">")?,
|
||||||
false => write!(f, ">")?,
|
false => write!(f, ">")?,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,7 +219,7 @@ impl<'a> TextRenderer<'a, '_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if self.cfg.newline {
|
if self.cfg.newline {
|
||||||
write!(f, "\n")?;
|
writeln!(f)?;
|
||||||
}
|
}
|
||||||
if self.cfg.indent {
|
if self.cfg.indent {
|
||||||
for _ in 0..il {
|
for _ in 0..il {
|
||||||
@@ -201,7 +229,7 @@ impl<'a> TextRenderer<'a, '_> {
|
|||||||
|
|
||||||
write!(f, "</{}>", el.tag_name)?;
|
write!(f, "</{}>", el.tag_name)?;
|
||||||
if self.cfg.newline {
|
if self.cfg.newline {
|
||||||
write!(f, "\n")?;
|
writeln!(f)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
VNode::Fragment(frag) => {
|
VNode::Fragment(frag) => {
|
||||||
@@ -211,14 +239,11 @@ impl<'a> TextRenderer<'a, '_> {
|
|||||||
}
|
}
|
||||||
VNode::Component(vcomp) => {
|
VNode::Component(vcomp) => {
|
||||||
let idx = vcomp.associated_scope.get().unwrap();
|
let idx = vcomp.associated_scope.get().unwrap();
|
||||||
match (self.vdom, self.cfg.skip_components) {
|
|
||||||
(Some(vdom), false) => {
|
if let (Some(vdom), false) = (self.vdom, self.cfg.skip_components) {
|
||||||
let new_node = vdom.get_scope(idx).unwrap().root_node();
|
let new_node = vdom.get_scope(idx).unwrap().root_node();
|
||||||
self.html_render(new_node, f, il + 1)?;
|
self.html_render(new_node, f, il + 1)?;
|
||||||
}
|
} else {
|
||||||
_ => {
|
|
||||||
// render the component by name
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
VNode::Suspended { .. } => {
|
VNode::Suspended { .. } => {
|
||||||
@@ -229,6 +254,7 @@ impl<'a> TextRenderer<'a, '_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct SsrConfig {
|
pub struct SsrConfig {
|
||||||
/// currently not supported - control if we indent the HTML output
|
/// currently not supported - control if we indent the HTML output
|
||||||
indent: bool,
|
indent: bool,
|
||||||
@@ -245,17 +271,6 @@ pub struct SsrConfig {
|
|||||||
skip_components: bool,
|
skip_components: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for SsrConfig {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
indent: false,
|
|
||||||
pre_render: false,
|
|
||||||
newline: false,
|
|
||||||
skip_components: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SsrConfig {
|
impl SsrConfig {
|
||||||
pub fn indent(mut self, a: bool) -> Self {
|
pub fn indent(mut self, a: bool) -> Self {
|
||||||
self.indent = a;
|
self.indent = a;
|
||||||
@@ -284,13 +299,13 @@ mod tests {
|
|||||||
use dioxus_core_macro::*;
|
use dioxus_core_macro::*;
|
||||||
use dioxus_html as dioxus_elements;
|
use dioxus_html as dioxus_elements;
|
||||||
|
|
||||||
static SIMPLE_APP: FC<()> = |(cx, props)| {
|
static SIMPLE_APP: FC<()> = |(cx, _)| {
|
||||||
cx.render(rsx!(div {
|
cx.render(rsx!(div {
|
||||||
"hello world!"
|
"hello world!"
|
||||||
}))
|
}))
|
||||||
};
|
};
|
||||||
|
|
||||||
static SLIGHTLY_MORE_COMPLEX: FC<()> = |(cx, props)| {
|
static SLIGHTLY_MORE_COMPLEX: FC<()> = |(cx, _)| {
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div {
|
div {
|
||||||
title: "About W3Schools"
|
title: "About W3Schools"
|
||||||
@@ -309,14 +324,14 @@ mod tests {
|
|||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
static NESTED_APP: FC<()> = |(cx, props)| {
|
static NESTED_APP: FC<()> = |(cx, _)| {
|
||||||
cx.render(rsx!(
|
cx.render(rsx!(
|
||||||
div {
|
div {
|
||||||
SIMPLE_APP {}
|
SIMPLE_APP {}
|
||||||
}
|
}
|
||||||
))
|
))
|
||||||
};
|
};
|
||||||
static FRAGMENT_APP: FC<()> = |(cx, props)| {
|
static FRAGMENT_APP: FC<()> = |(cx, _)| {
|
||||||
cx.render(rsx!(
|
cx.render(rsx!(
|
||||||
div { "f1" }
|
div { "f1" }
|
||||||
div { "f2" }
|
div { "f2" }
|
||||||
@@ -372,7 +387,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn styles() {
|
fn styles() {
|
||||||
static STLYE_APP: FC<()> = |(cx, props)| {
|
static STLYE_APP: FC<()> = |(cx, _)| {
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div { style: { color: "blue", font_size: "46px" } }
|
div { style: { color: "blue", font_size: "46px" } }
|
||||||
})
|
})
|
||||||
@@ -385,17 +400,34 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lazy() {
|
fn lazy() {
|
||||||
let p1 = SsrRenderer::new().render_lazy(rsx! {
|
let p1 = SsrRenderer::new(|c| c).render_lazy(rsx! {
|
||||||
div {
|
div { "ello" }
|
||||||
"ello"
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let p2 = render_lazy!(rsx! {
|
let p2 = render_lazy(rsx! {
|
||||||
div {
|
div {
|
||||||
"ello"
|
"ello"
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
assert_eq!(p1, p2);
|
assert_eq!(p1, p2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn big_lazy() {
|
||||||
|
let s = render_lazy(rsx! {
|
||||||
|
div {
|
||||||
|
div {
|
||||||
|
div {
|
||||||
|
h1 { "ello world" }
|
||||||
|
h1 { "ello world" }
|
||||||
|
h1 { "ello world" }
|
||||||
|
h1 { "ello world" }
|
||||||
|
h1 { "ello world" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
dbg!(s);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user