feat: implement hydration for vraw (#3245)

This commit is contained in:
Dillen Meijboom 2023-04-25 23:48:04 +02:00 committed by GitHub
parent 998db3db44
commit d0205a8ea3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 104 additions and 5 deletions

View File

@ -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(),
}
}
}

View File

@ -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 {

View File

@ -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 => {}
}

View File

@ -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);
}
}
}
}

View File

@ -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)]