mirror of
https://github.com/yewstack/yew.git
synced 2025-12-08 21:26:25 +00:00
feat: implement hydration for vraw (#3245)
This commit is contained in:
parent
998db3db44
commit
d0205a8ea3
@ -267,9 +267,7 @@ mod feat_hydration {
|
||||
VNode::VSuspense(vsuspense) => vsuspense
|
||||
.hydrate(root, parent_scope, parent, fragment)
|
||||
.into(),
|
||||
VNode::VRaw(_) => {
|
||||
panic!("VRaw is not hydratable (raw HTML string cannot be hydrated)")
|
||||
}
|
||||
VNode::VRaw(vraw) => vraw.hydrate(root, parent_scope, parent, fragment).into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -125,6 +125,34 @@ impl Reconcilable for VRaw {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "hydration")]
|
||||
mod feat_hydration {
|
||||
use super::*;
|
||||
use crate::dom_bundle::{Fragment, Hydratable};
|
||||
use crate::virtual_dom::Collectable;
|
||||
|
||||
impl Hydratable for VRaw {
|
||||
fn hydrate(
|
||||
self,
|
||||
_root: &BSubtree,
|
||||
_parent_scope: &AnyScope,
|
||||
parent: &Element,
|
||||
fragment: &mut Fragment,
|
||||
) -> Self::Bundle {
|
||||
let collectable = Collectable::Raw;
|
||||
let fallback_fragment = Fragment::collect_between(fragment, &collectable, parent);
|
||||
|
||||
let Self { html } = self;
|
||||
|
||||
BRaw {
|
||||
children_count: fallback_fragment.len(),
|
||||
reference: fallback_fragment.iter().next().cloned(),
|
||||
html,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
@ -64,6 +64,7 @@ mod feat_ssr_hydration {
|
||||
/// This indicates a kind that can be collected from fragment to be processed at a later time
|
||||
pub enum Collectable {
|
||||
Component(ComponentName),
|
||||
Raw,
|
||||
Suspense,
|
||||
}
|
||||
|
||||
@ -79,6 +80,7 @@ mod feat_ssr_hydration {
|
||||
pub fn open_start_mark(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Component(_) => "<[",
|
||||
Self::Raw => "<#",
|
||||
Self::Suspense => "<?",
|
||||
}
|
||||
}
|
||||
@ -86,6 +88,7 @@ mod feat_ssr_hydration {
|
||||
pub fn close_start_mark(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Component(_) => "</[",
|
||||
Self::Raw => "</#",
|
||||
Self::Suspense => "</?",
|
||||
}
|
||||
}
|
||||
@ -93,6 +96,7 @@ mod feat_ssr_hydration {
|
||||
pub fn end_mark(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Component(_) => "]>",
|
||||
Self::Raw => ">",
|
||||
Self::Suspense => ">",
|
||||
}
|
||||
}
|
||||
@ -104,6 +108,7 @@ mod feat_ssr_hydration {
|
||||
Self::Component(m) => format!("Component({m})").into(),
|
||||
#[cfg(not(debug_assertions))]
|
||||
Self::Component(_) => "Component".into(),
|
||||
Self::Raw => "Raw".into(),
|
||||
Self::Suspense => "Suspense".into(),
|
||||
}
|
||||
}
|
||||
@ -130,6 +135,7 @@ mod feat_ssr {
|
||||
Self::Component(type_name) => {
|
||||
let _ = w.write_str(type_name);
|
||||
}
|
||||
Self::Raw => {}
|
||||
Self::Suspense => {}
|
||||
}
|
||||
|
||||
@ -146,6 +152,7 @@ mod feat_ssr {
|
||||
Self::Component(type_name) => {
|
||||
let _ = w.write_str(type_name);
|
||||
}
|
||||
Self::Raw => {}
|
||||
Self::Suspense => {}
|
||||
}
|
||||
|
||||
|
||||
@ -19,15 +19,26 @@ mod feat_ssr {
|
||||
use super::*;
|
||||
use crate::html::AnyScope;
|
||||
use crate::platform::fmt::BufWriter;
|
||||
use crate::virtual_dom::Collectable;
|
||||
|
||||
impl VRaw {
|
||||
pub(crate) async fn render_into_stream(
|
||||
&self,
|
||||
w: &mut BufWriter,
|
||||
_parent_scope: &AnyScope,
|
||||
_hydratable: bool,
|
||||
hydratable: bool,
|
||||
) {
|
||||
let collectable = Collectable::Raw;
|
||||
|
||||
if hydratable {
|
||||
collectable.write_open_tag(w);
|
||||
}
|
||||
|
||||
let _ = w.write_str(self.html.as_ref());
|
||||
|
||||
if hydratable {
|
||||
collectable.write_close_tag(w);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,7 +15,8 @@ use web_sys::{HtmlElement, HtmlTextAreaElement};
|
||||
use yew::platform::time::sleep;
|
||||
use yew::prelude::*;
|
||||
use yew::suspense::{use_future, Suspension, SuspensionResult};
|
||||
use yew::{Renderer, ServerRenderer};
|
||||
use yew::virtual_dom::VNode;
|
||||
use yew::{function_component, Renderer, ServerRenderer};
|
||||
|
||||
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
|
||||
|
||||
@ -94,6 +95,60 @@ async fn hydration_works() {
|
||||
);
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
async fn hydration_with_raw() {
|
||||
#[function_component(Content)]
|
||||
fn content() -> Html {
|
||||
let vnode = VNode::from_html_unchecked("<div><p>Hello World</p></div>".into());
|
||||
|
||||
html! {
|
||||
<div class="content-area">
|
||||
{vnode}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
#[function_component(App)]
|
||||
fn app() -> Html {
|
||||
html! {
|
||||
<div id="result">
|
||||
<Content />
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
let s = ServerRenderer::<App>::new().render().await;
|
||||
|
||||
gloo::utils::document()
|
||||
.query_selector("#output")
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.set_inner_html(&s);
|
||||
|
||||
sleep(Duration::from_millis(10)).await;
|
||||
|
||||
Renderer::<App>::with_root(gloo::utils::document().get_element_by_id("output").unwrap())
|
||||
.hydrate();
|
||||
|
||||
let result = obtain_result();
|
||||
|
||||
// still hydrating, during hydration, the server rendered result is shown.
|
||||
assert_eq!(
|
||||
result.as_str(),
|
||||
r#"<!--<[hydration::hydration_with_raw::{{closure}}::Content]>--><div class="content-area"><!--<#>--><div><p>Hello World</p></div><!--</#>--></div><!--</[hydration::hydration_with_raw::{{closure}}::Content]>-->"#
|
||||
);
|
||||
|
||||
sleep(Duration::from_millis(50)).await;
|
||||
|
||||
let result = obtain_result();
|
||||
|
||||
// hydrated.
|
||||
assert_eq!(
|
||||
result.as_str(),
|
||||
r#"<div class="content-area"><div><p>Hello World</p></div></div>"#
|
||||
);
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
async fn hydration_with_suspense() {
|
||||
#[derive(PartialEq)]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user