perf: reduce overhead of hydration keys (#1122)

This commit is contained in:
Greg Johnston 2023-05-31 20:30:31 -04:00 committed by GitHub
parent f3e544b003
commit 55266f2efd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 60 additions and 61 deletions

View file

@ -36,7 +36,7 @@ pub fn Stories(cx: Scope) -> impl IntoView {
);
let (pending, set_pending) = create_signal(cx, false);
let hide_more_link = move || {
let hide_more_link = move |cx| {
pending()
|| stories.read(cx).unwrap_or(None).unwrap_or_default().len() < 28
};
@ -66,16 +66,20 @@ pub fn Stories(cx: Scope) -> impl IntoView {
}}
</span>
<span>"page " {page}</span>
<span class="page-link"
class:disabled=hide_more_link
aria-hidden=hide_more_link
<Transition
fallback=move || view! { cx, <p>"Loading..."</p> }
>
<a href=move || format!("/{}?page={}", story_type(), page() + 1)
aria-label="Next Page"
<span class="page-link"
class:disabled=move || hide_more_link(cx)
aria-hidden=move || hide_more_link(cx)
>
"more >"
</a>
</span>
<a href=move || format!("/{}?page={}", story_type(), page() + 1)
aria-label="Next Page"
>
"more >"
</a>
</span>
</Transition>
</div>
<main class="news-list">
<div>

View file

@ -102,7 +102,7 @@ where
// run the child; we'll probably throw this away, but it will register resource reads
let _child = orig_child(cx).into_view(cx);
let after_original_child = HydrationCtx::id();
let after_original_child = HydrationCtx::peek();
let initial = {
// no resources were read under this, so just return the child
@ -116,13 +116,13 @@ where
// show the fallback, but also prepare to stream HTML
else {
let orig_child = Rc::clone(&orig_child);
HydrationCtx::continue_from(current_id);
cx.register_suspense(
context,
&current_id.to_string(),
// out-of-order streaming
{
let current_id = current_id.clone();
let orig_child = Rc::clone(&orig_child);
move || {
HydrationCtx::continue_from(current_id.clone());
@ -136,7 +136,6 @@ where
},
// in-order streaming
{
let current_id = current_id.clone();
move || {
HydrationCtx::continue_from(current_id.clone());
Fragment::lazy(Box::new(move || {
@ -165,7 +164,8 @@ where
_ => unreachable!(),
};
HydrationCtx::continue_from(current_id.clone());
HydrationCtx::continue_from(current_id);
HydrationCtx::next_component();
leptos_dom::View::Suspense(current_id, core_component)
}

View file

@ -52,11 +52,11 @@ fn ssr_test_with_components() {
};
assert!(rendered.into_view(cx).render_to_string(cx).contains(
"<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>"
"<div id=\"_0-3\"><button id=\"_0-4\">-1</button><span \
id=\"_0-5\">Value: \
<!--hk=_0-6o|leptos-dyn-child-start-->1<!\
--hk=_0-6c|leptos-dyn-child-end-->!</span><button \
id=\"_0-7\">+1</button></div>"
));
});
}
@ -89,11 +89,11 @@ fn ssr_test_with_snake_case_components() {
};
assert!(rendered.into_view(cx).render_to_string(cx).contains(
"<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>"
"<div id=\"_0-3\"><button id=\"_0-4\">-1</button><span \
id=\"_0-5\">Value: \
<!--hk=_0-6o|leptos-dyn-child-start-->1<!\
--hk=_0-6c|leptos-dyn-child-end-->!</span><button \
id=\"_0-7\">+1</button></div>"
));
});
}

View file

@ -113,6 +113,7 @@ fn SecondaryNav(cx: Scope) -> impl IntoView {
fn Nested(cx: Scope) -> impl IntoView {
let one_second = create_resource(cx, || (), one_second_fn);
let two_second = create_resource(cx, || (), two_second_fn);
let (count, set_count) = create_signal(cx, 0);
view! { cx,
<div>
@ -125,7 +126,12 @@ fn Nested(cx: Scope) -> impl IntoView {
<Suspense fallback=|| "Loading 2...">
"Two Second: "
{move || {
two_second.read(cx).map(|_| "Loaded 2!")
two_second.read(cx).map(|_| view! { cx,
"Loaded 2!"
<button on:click=move |_| set_count.update(|n| *n += 1)>
{count}
</button>
})
}}
</Suspense>
</Suspense>

View file

@ -242,7 +242,7 @@ where
/// Creates a new component.
pub fn new(name: impl Into<Cow<'static, str>>, f: F) -> Self {
Self {
id: HydrationCtx::next_component(),
id: HydrationCtx::id(),
name: name.into(),
children_fn: f,
}

View file

@ -80,7 +80,7 @@ where
E: Error + Send + Sync + 'static,
{
fn into_view(self, cx: leptos_reactive::Scope) -> crate::View {
let id = ErrorKey(HydrationCtx::peek().previous.into());
let id = ErrorKey(HydrationCtx::peek().id.to_string().into());
let errors = use_context::<RwSignal<Errors>>(cx);
match self {
Ok(stuff) => {

View file

@ -47,7 +47,7 @@ impl From<View> for Fragment {
impl From<Fragment> for View {
fn from(value: Fragment) -> Self {
let mut frag = ComponentRepr::new_with_id("", value.id.clone());
let mut frag = ComponentRepr::new_with_id("", value.id);
#[cfg(debug_assertions)]
{

View file

@ -449,7 +449,7 @@ impl<El: ElementDescriptor + 'static> HtmlElement<El> {
element: AnyElement {
name: element.name(),
is_void: element.is_void(),
id: element.hydration_id().clone()
id: *element.hydration_id()
},
#[cfg(debug_assertions)]
view_marker
@ -1072,7 +1072,7 @@ impl<El: ElementDescriptor> IntoView for HtmlElement<El> {
..
} = self;
let id = element.hydration_id().clone();
let id = *element.hydration_id();
let mut element = Element::new(element);
let children = children;
@ -1116,7 +1116,7 @@ pub fn custom<El: ElementDescriptor>(cx: Scope, el: El) -> HtmlElement<Custom> {
#[cfg(all(target_arch = "wasm32", feature = "web"))]
element: el.as_ref().clone(),
#[cfg(not(all(target_arch = "wasm32", feature = "web")))]
id: el.hydration_id().clone(),
id: *el.hydration_id(),
},
)
}

View file

@ -50,13 +50,13 @@ cfg_if! {
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());
return crate::document().get_element_by_id("_0-1").is_some()
|| crate::document().get_element_by_id("_0-1o").is_some()
|| HYDRATION_COMMENTS.with(|comments| comments.get("_0-1o").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());
return crate::document().get_element_by_id("_0-1").is_some()
|| HYDRATION_COMMENTS.with(|comments| comments.get("_0-1").is_some());
}));
}
@ -67,26 +67,17 @@ cfg_if! {
}
/// A stable identifier within the server-rendering or hydration process.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Default)]
pub struct HydrationKey {
/// The key of the previous component.
pub previous: String,
/// The element offset within the current component.
pub offset: usize,
/// ID of the current key.
pub id: usize,
/// ID of the current fragment.
pub fragment: usize,
}
impl Display for HydrationKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}{}", self.previous, self.offset)
}
}
impl Default for HydrationKey {
fn default() -> Self {
Self {
previous: "0-".to_string(),
offset: 0,
}
write!(f, "{}-{}", self.fragment, self.id)
}
}
@ -98,15 +89,15 @@ pub struct HydrationCtx;
impl HydrationCtx {
/// Get the next `id` without incrementing it.
pub fn peek() -> HydrationKey {
ID.with(|id| id.borrow().clone())
ID.with(|id| *id.borrow())
}
/// Increments the current hydration `id` and returns it
pub fn id() -> HydrationKey {
ID.with(|id| {
let mut id = id.borrow_mut();
id.offset = id.offset.wrapping_add(1);
id.clone()
id.id = id.id.wrapping_add(1);
*id
})
}
@ -114,11 +105,9 @@ impl HydrationCtx {
pub fn next_component() -> HydrationKey {
ID.with(|id| {
let mut id = id.borrow_mut();
let offset = id.offset;
id.previous.push_str(&offset.to_string());
id.previous.push('-');
id.offset = 0;
id.clone()
id.fragment = id.fragment.wrapping_add(1);
id.id = 0;
*id
})
}

View file

@ -376,7 +376,7 @@ impl Element {
is_void: el.is_void(),
attrs: Default::default(),
children: Default::default(),
id: el.hydration_id().clone(),
id: *el.hydration_id(),
#[cfg(debug_assertions)]
view_marker: None
}

View file

@ -393,7 +393,7 @@ impl View {
View::CoreComponent(node) => {
let (id, name, wrap, content) = match node {
CoreComponent::Unit(u) => (
u.id.clone(),
u.id,
"",
false,
Box::new(move || {

View file

@ -374,7 +374,7 @@ impl View {
View::CoreComponent(node) => {
let (id, name, wrap, content) = match node {
CoreComponent::Unit(u) => (
u.id.clone(),
u.id,
"",
false,
Box::new(move |chunks: &mut VecDeque<StreamChunk>| {