feat: overhaul ssr

This commit is contained in:
Jonathan Kelley committed 2022-12-06 17:41:47 -08:00
1 parent e8ae1fb83e
commit 34d9aafe0e
16 files changed
+263 -561

No files matched your search

-139
View File
@@ -1,140 +1 @@
use super::cache::Segment;
use dioxus_core::{prelude::*, AttributeValue, DynamicNode, RenderReturn, VText};
use std::collections::HashMap;
use std::fmt::Write;
use std::rc::Rc;
use crate::cache::StringCache;
/// A virtualdom renderer that caches the templates it has seen for faster rendering
#[derive(Default)]
pub struct SsrRender {
template_cache: HashMap<&'static str, Rc<StringCache>>,
}
impl SsrRender {
pub fn render_vdom(&mut self, dom: &VirtualDom) -> String {
let scope = dom.base_scope();
let root = scope.root_node();
let mut out = String::new();
match root {
RenderReturn::Sync(Ok(node)) => self.render_template(&mut out, dom, node).unwrap(),
_ => {}
};
out
}
fn render_template(
&mut self,
buf: &mut String,
dom: &VirtualDom,
template: &VNode,
) -> std::fmt::Result {
let entry = self
.template_cache
.entry(template.template.name)
.or_insert_with(|| Rc::new(StringCache::from_template(template).unwrap()))
.clone();
for segment in entry.segments.iter() {
match segment {
Segment::Attr(idx) => {
let attr = &template.dynamic_attrs[*idx];
match attr.value {
AttributeValue::Text(value) => write!(buf, " {}=\"{}\"", attr.name, value)?,
AttributeValue::Bool(value) => write!(buf, " {}={}", attr.name, value)?,
_ => {}
};
}
Segment::Node(idx) => match &template.dynamic_nodes[*idx] {
DynamicNode::Component(node) => {
let id = node.scope.get().unwrap();
let scope = dom.get_scope(id).unwrap();
let node = scope.root_node();
match node {
RenderReturn::Sync(Ok(node)) => self.render_template(buf, dom, node)?,
_ => todo!(),
}
}
DynamicNode::Text(text) => {
// in SSR, we are concerned that we can't hunt down the right text node since they might get merged
// if !*inner {
// write!(buf, "<!--#-->")?;
// }
// todo: escape the text
write!(buf, "{}", text.value)?;
// if !*inner {
// write!(buf, "<!--/#-->")?;
// }
}
DynamicNode::Fragment(nodes) => {
for child in *nodes {
self.render_template(buf, dom, child)?;
}
}
DynamicNode::Placeholder(_el) => {
// todo write a placeholder if in pre-render mode
// write!(buf, "<!--placeholder-->")?;
}
},
Segment::PreRendered(contents) => buf.push_str(contents),
}
}
Ok(())
}
}
#[test]
fn to_string_works() {
use dioxus::prelude::*;
fn app(cx: Scope) -> Element {
let dynamic = 123;
let dyn2 = "</diiiiiiiiv>"; // todo: escape this
render! {
div { class: "asdasdasd", class: "asdasdasd", id: "id-{dynamic}",
"Hello world 1 -->" "{dynamic}" "<-- Hello world 2"
div { "nest 1" }
div {}
div { "nest 2" }
"{dyn2}"
(0..5).map(|i| rsx! { div { "finalize {i}" } })
}
}
}
let mut dom = VirtualDom::new(app);
dom.rebuild();
use Segment::*;
// assert_eq!(
// StringCache::from_template(&dom.base_scope().root_node())
// .unwrap()
// .segments,
// vec![
// PreRendered("<div class=\"asdasdasd\" class=\"asdasdasd\"".into(),),
// Attr(0,),
// PreRendered(">Hello world 1 -->".into(),),
// Node(0,),
// PreRendered("<-- Hello world 2<div>nest 1</div><div></div><div>nest 2</div>".into(),),
// Node(1,),
// Node(2,),
// PreRendered("</div>".into(),),
// ]
// );
// assert_eq!(
// SsrRender::default().render_vdom(&dom),
// "<div class=\"asdasdasd\" class=\"asdasdasd\" id=\"id-123\">Hello world 1 --><!--#-->123<!--/#--><-- Hello world 2<div>nest 1</div><div></div><div>nest 2</div><!--#--></diiiiiiiiv><!--/#--><div><!--#-->finalize 0<!--/#--></div><div><!--#-->finalize 1<!--/#--></div><div><!--#-->finalize 2<!--/#--></div><div><!--#-->finalize 3<!--/#--></div><div><!--#-->finalize 4<!--/#--></div></div>"
// );
}