Merge pull request #321 from leptos-rs/for-ssr

This commit is contained in:
Greg Johnston 2023-01-16 21:49:24 -05:00 committed by GitHub
commit 27ec506fd5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 62 additions and 50 deletions

View File

@ -16,7 +16,7 @@ fn simple_ssr_test() {
assert_eq!(
rendered.into_view(cx).render_to_string(cx),
"<div id=\"_0-1\"><button id=\"_0-2\">-1</button><span id=\"_0-3\">Value: <leptos-dyn-child-start leptos id=\"_0-4o\"></leptos-dyn-child-start>0<leptos-dyn-child-end leptos id=\"_0-4c\"></leptos-dyn-child-end>!</span><button id=\"_0-5\">+1</button></div>"
"<div id=\"_0-1\"><button id=\"_0-2\">-1</button><span id=\"_0-3\">Value: <!--hk=_0-4o|leptos-dyn-child-start-->0<!--hk=_0-4c|leptos-dyn-child-end-->!</span><button id=\"_0-5\">+1</button></div>"
);
});
}
@ -50,7 +50,7 @@ fn ssr_test_with_components() {
assert_eq!(
rendered.into_view(cx).render_to_string(cx),
"<div class=\"counters\" id=\"_0-1\"><leptos-counter-start leptos id=\"_0-1-0o\"></leptos-counter-start><div id=\"_0-1-1\"><button id=\"_0-1-2\">-1</button><span id=\"_0-1-3\">Value: <leptos-dyn-child-start leptos id=\"_0-1-4o\"></leptos-dyn-child-start>1<leptos-dyn-child-end leptos id=\"_0-1-4c\"></leptos-dyn-child-end>!</span><button id=\"_0-1-5\">+1</button></div><leptos-counter-end leptos id=\"_0-1-0c\"></leptos-counter-end><leptos-counter-start leptos id=\"_0-1-5-0o\"></leptos-counter-start><div id=\"_0-1-5-1\"><button id=\"_0-1-5-2\">-1</button><span id=\"_0-1-5-3\">Value: <leptos-dyn-child-start leptos id=\"_0-1-5-4o\"></leptos-dyn-child-start>2<leptos-dyn-child-end leptos id=\"_0-1-5-4c\"></leptos-dyn-child-end>!</span><button id=\"_0-1-5-5\">+1</button></div><leptos-counter-end leptos id=\"_0-1-5-0c\"></leptos-counter-end></div>"
"<div class=\"counters\" id=\"_0-1\"><!--hk=_0-1-0o|leptos-counter-start--><div id=\"_0-1-1\"><button id=\"_0-1-2\">-1</button><span id=\"_0-1-3\">Value: <!--hk=_0-1-4o|leptos-dyn-child-start-->1<!--hk=_0-1-4c|leptos-dyn-child-end-->!</span><button id=\"_0-1-5\">+1</button></div><!--hk=_0-1-0c|leptos-counter-end--><!--hk=_0-1-5-0o|leptos-counter-start--><div id=\"_0-1-5-1\"><button id=\"_0-1-5-2\">-1</button><span id=\"_0-1-5-3\">Value: <!--hk=_0-1-5-4o|leptos-dyn-child-start-->2<!--hk=_0-1-5-4c|leptos-dyn-child-end-->!</span><button id=\"_0-1-5-5\">+1</button></div><!--hk=_0-1-5-0c|leptos-counter-end--></div>"
);
});
}

View File

@ -39,6 +39,7 @@ features = [
"Range",
"Text",
"HtmlCollection",
"TreeWalker",
# Events we cast to in leptos_macro -- added here so we don't force users to import them
"AnimationEvent",

View File

@ -1,21 +1,51 @@
use cfg_if::cfg_if;
use std::{cell::RefCell, fmt::Display};
#[cfg(all(target_arch = "wasm32", feature = "web"))]
use once_cell::unsync::Lazy as LazyCell;
cfg_if! {
if #[cfg(all(target_arch = "wasm32", feature = "web"))] {
use once_cell::unsync::Lazy as LazyCell;
use std::collections::HashMap;
use wasm_bindgen::JsCast;
// We can tell if we start in hydration mode by checking to see if the
// id "_0-0-0" is present in the DOM. If it is, we know we are hydrating from
// the server, if not, we are starting off in CSR
#[cfg(all(target_arch = "wasm32", feature = "web"))]
thread_local! {
static IS_HYDRATING: RefCell<LazyCell<bool>> = RefCell::new(LazyCell::new(|| {
#[cfg(debug_assertions)]
return crate::document().get_element_by_id("_0-0-0").is_some()
|| crate::document().get_element_by_id("_0-0-0o").is_some();
// We can tell if we start in hydration mode by checking to see if the
// id "_0-0-0" is present in the DOM. If it is, we know we are hydrating from
// the server, if not, we are starting off in CSR
thread_local! {
static HYDRATION_COMMENTS: LazyCell<HashMap<String, web_sys::Comment>> = LazyCell::new(|| {
let document = crate::document();
let body = document.body().unwrap();
let walker = document
.create_tree_walker_with_what_to_show(&body, 128)
.unwrap();
let mut map = HashMap::new();
while let Ok(Some(node)) = walker.next_node() {
if let Some(content) = node.text_content() {
if let Some(hk) = content.strip_prefix("hk=") {
if let Some(hk) = hk.split("|").next() {
map.insert(hk.into(), node.unchecked_into());
}
}
}
}
map
});
#[cfg(not(debug_assertions))]
return crate::document().get_element_by_id("_0-0-0").is_some();
}));
static IS_HYDRATING: RefCell<LazyCell<bool>> = RefCell::new(LazyCell::new(|| {
#[cfg(debug_assertions)]
return crate::document().get_element_by_id("_0-0-0").is_some()
|| crate::document().get_element_by_id("_0-0-0o").is_some()
|| HYDRATION_COMMENTS.with(|comments| comments.get("_0-0-0o").is_some());
#[cfg(not(debug_assertions))]
return crate::document().get_element_by_id("_0-0-0").is_some()
|| HYDRATION_COMMENTS.with(|comments| comments.get("_0-0-0").is_some());
}));
}
pub(crate) fn get_marker(id: &str) -> Option<web_sys::Comment> {
HYDRATION_COMMENTS.with(|comments| comments.get(id).cloned())
}
}
}
/// A stable identifer within the server-rendering or hydration process.

View File

@ -303,7 +303,7 @@ impl Comment {
if HydrationCtx::is_hydrating() {
let id = HydrationCtx::to_string(id, closing);
if let Some(marker) = document().get_element_by_id(&id) {
if let Some(marker) = hydration::get_marker(&id) {
marker.before_with_node_1(&node).unwrap();
marker.remove();
@ -548,7 +548,7 @@ impl View {
pub fn on<E: ev::EventDescriptor + 'static>(
self,
event: E,
mut event_handler: impl FnMut(E::EventType) + 'static,
#[allow(unused_mut)] mut event_handler: impl FnMut(E::EventType) + 'static,
) -> Self {
cfg_if::cfg_if! {
if #[cfg(debug_assertions)] {

View File

@ -16,7 +16,7 @@ use std::borrow::Cow;
/// <p>"Hello, world!"</p>
/// });
/// // static HTML includes some hydration info
/// assert_eq!(html, "<style>[leptos]{display:none;}</style><p id=\"_0-1\">Hello, world!</p>");
/// assert_eq!(html, "<p id=\"_0-1\">Hello, world!</p>");
/// # }}
/// ```
pub fn render_to_string<F, N>(f: F) -> String
@ -33,13 +33,7 @@ where
runtime.dispose();
#[cfg(debug_assertions)]
{
format!("<style>[leptos]{{display:none;}}</style>{html}")
}
#[cfg(not(debug_assertions))]
format!("<style>l-m{{display:none;}}</style>{html}")
html.into()
}
/// Renders a function to a stream of HTML strings.
@ -122,16 +116,6 @@ pub fn render_to_stream_with_prefix_undisposed(
let pending_resources = serde_json::to_string(&resources).unwrap();
let prefix = prefix(cx);
let shell = {
#[cfg(debug_assertions)]
{
format!("<style>[leptos]{{display:none;}}</style>{shell}")
}
#[cfg(not(debug_assertions))]
format!("<style>l-m{{display:none;}}</style>{shell}")
};
(
shell,
prefix,
@ -218,7 +202,7 @@ impl View {
};
cfg_if! {
if #[cfg(debug_assertions)] {
format!(r#"<leptos-{name}-start leptos id="{}"></leptos-{name}-start>{}<leptos-{name}-end leptos id="{}"></leptos-{name}-end>"#,
format!(r#"<!--hk={}|leptos-{name}-start-->{}<!--hk={}|leptos-{name}-end-->"#,
HydrationCtx::to_string(&node.id, false),
content(),
HydrationCtx::to_string(&node.id, true),
@ -226,7 +210,7 @@ impl View {
).into()
} else {
format!(
r#"{}<l-m id="{}"></l-m>"#,
r#"{}<!--hk={}-->"#,
content(),
HydrationCtx::to_string(&node.id, true)
).into()
@ -243,14 +227,14 @@ impl View {
#[cfg(debug_assertions)]
{
format!(
"<leptos-unit leptos id={}></leptos-unit>",
"<!--hk={}|leptos-unit-->",
HydrationCtx::to_string(&u.id, true)
)
.into()
}
#[cfg(not(debug_assertions))]
format!("<l-m id={}></l-m>", HydrationCtx::to_string(&u.id, true))
format!("<!--hk={}-->", HydrationCtx::to_string(&u.id, true))
.into()
}) as Box<dyn FnOnce() -> Cow<'static, str>>,
),
@ -286,7 +270,6 @@ impl View {
}
CoreComponent::Each(node) => {
let children = node.children.take();
(
node.id,
"each",
@ -303,10 +286,8 @@ impl View {
#[cfg(debug_assertions)]
{
format!(
"<leptos-each-item-start leptos \
id=\"{}\"></\
leptos-each-item-start>{}<leptos-each-item-end \
leptos id=\"{}\"></leptos-each-item-end>",
"<!--hk={}|leptos-each-item-start-->{}<!\
--hk={}|leptos-each-item-end-->",
HydrationCtx::to_string(&id, false),
content(),
HydrationCtx::to_string(&id, true),
@ -315,7 +296,7 @@ impl View {
#[cfg(not(debug_assertions))]
format!(
"{}<l-m id=\"{}\"></l-m>",
"{}<!--hk={}-->",
content(),
HydrationCtx::to_string(&id, true)
)
@ -331,7 +312,7 @@ impl View {
cfg_if! {
if #[cfg(debug_assertions)] {
format!(
r#"<leptos-{name}-start leptos id="{}"></leptos-{name}-start>{}<leptos-{name}-end leptos id="{}"></leptos-{name}-end>"#,
r#"<!--hk={}|leptos-{name}-start-->{}<!--hk={}|leptos-{name}-end-->"#,
HydrationCtx::to_string(&id, false),
content(),
HydrationCtx::to_string(&id, true),
@ -340,7 +321,7 @@ impl View {
let _ = name;
format!(
r#"{}<l-m id="{}"></l-m>"#,
r#"{}<!--hk={}-->"#,
content(),
HydrationCtx::to_string(&id, true)
).into()

View File

@ -208,10 +208,10 @@ impl MetaContext {
/// // `app` contains only the body content w/ hydration stuff, not the meta tags
/// assert_eq!(
/// app.into_view(cx).render_to_string(cx),
/// "<main id=\"_0-1\"><leptos-unit leptos id=_0-2c></leptos-unit><leptos-unit leptos id=_0-4c></leptos-unit><p id=\"_0-5\">Some text</p></main>"
/// "<main id=\"_0-1\"><!--hk=_0-2c|leptos-unit--><!--hk=_0-4c|leptos-unit--><p id=\"_0-5\">Some text</p></main>"
/// );
/// // `MetaContext::dehydrate()` gives you HTML that should be in the `<head>`
/// assert_eq!(use_head(cx).dehydrate(), r#"<title>my title</title><link id="leptos-link-1" href="/style.css" rel="stylesheet" leptos-hk="_0-3"/>"#)
/// assert_eq!(use_head(cx).dehydrate(), "<title>my title</title><link id=\"leptos-link-1\" href=\"/style.css\" rel=\"stylesheet\" leptos-hk=\"_0-3\"/>")
/// });
/// # }
/// ```