Skip to content

Commit

Permalink
perf: removed unnecessary content pre-rendering from server render pr…
Browse files Browse the repository at this point in the history
…ocess

For subsequent loads, the browser just needs the state to render the
page itself, and the Wasm is already instantiated, so there's no reason
to generate the content on the server as well, which slows page loads
down and increases the amount needing to be transferred over the
network. The gains from this commit are minimal, but present.
  • Loading branch information
arctic-hen7 committed Aug 20, 2022
1 parent 0c7c89c commit e1c9ad3
Show file tree
Hide file tree
Showing 11 changed files with 148 additions and 48 deletions.
1 change: 1 addition & 0 deletions packages/perseus-actix-web/src/initial_load.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ pub async fn initial_load<M: MutableStore, T: TranslationsManager>(
translations_manager: translations_manager.get_ref(),
},
template,
true, // This is an initial load, so we do want the content rendered/fetched
)
.await;
let page_data = match page_data {
Expand Down
10 changes: 8 additions & 2 deletions packages/perseus-actix-web/src/page_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use fmterr::fmt_err;
use perseus::{
errors::err_to_status_code,
i18n::TranslationsManager,
internal::PageDataPartial,
server::{get_page_for_template, GetPageProps, ServerOptions},
stores::{ImmutableStore, MutableStore},
};
Expand Down Expand Up @@ -68,18 +69,23 @@ pub async fn page_data<M: MutableStore, T: TranslationsManager>(
translations_manager: translations_manager.get_ref(),
},
template,
false, // For subsequent loads, we don't want to render content (the client can do it)
)
.await;
match page_data {
Ok(page_data) => {
let partial_page_data = PageDataPartial {
state: page_data.state,
head: page_data.head,
};
let mut http_res = HttpResponse::Ok();
http_res.content_type("text/html");
// Generate and add HTTP headers
for (key, val) in template.get_headers(page_data.state.clone()) {
for (key, val) in template.get_headers(partial_page_data.state.clone()) {
http_res.insert_header((key.unwrap(), val));
}

http_res.body(serde_json::to_string(&page_data).unwrap())
http_res.body(serde_json::to_string(&partial_page_data).unwrap())
}
// We parse the error to return an appropriate status code
Err(err) => {
Expand Down
1 change: 1 addition & 0 deletions packages/perseus-axum/src/initial_load.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ pub async fn initial_load_handler<M: MutableStore, T: TranslationsManager>(
translations_manager: &translations_manager,
},
template,
true,
)
.await;
let page_data = match page_data {
Expand Down
10 changes: 8 additions & 2 deletions packages/perseus-axum/src/page_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use fmterr::fmt_err;
use perseus::{
errors::err_to_status_code,
i18n::TranslationsManager,
internal::PageDataPartial,
server::{get_page_for_template, GetPageProps, ServerOptions},
stores::{ImmutableStore, MutableStore},
Request,
Expand Down Expand Up @@ -85,18 +86,23 @@ pub async fn page_handler<M: MutableStore, T: TranslationsManager>(
translations_manager: &translations_manager,
},
template,
false,
)
.await;
match page_data {
Ok(page_data) => {
let partial_page_data = PageDataPartial {
state: page_data.state,
head: page_data.head,
};
// http_res.content_type("text/html");
// Generate and add HTTP headers
let mut header_map = HeaderMap::new();
for (key, val) in template.get_headers(page_data.state.clone()) {
for (key, val) in template.get_headers(partial_page_data.state.clone()) {
header_map.insert(key.unwrap(), val);
}

let page_data_str = serde_json::to_string(&page_data).unwrap();
let page_data_str = serde_json::to_string(&partial_page_data).unwrap();

(StatusCode::OK, header_map, page_data_str)
}
Expand Down
1 change: 1 addition & 0 deletions packages/perseus-warp/src/initial_load.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ pub async fn initial_load_handler<M: MutableStore, T: TranslationsManager>(
translations_manager: &translations_manager,
},
template,
true,
)
.await;
let page_data = match page_data {
Expand Down
10 changes: 8 additions & 2 deletions packages/perseus-warp/src/page_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use fmterr::fmt_err;
use perseus::{
errors::err_to_status_code,
i18n::TranslationsManager,
internal::PageDataPartial,
server::{get_page_for_template, GetPageProps, ServerOptions},
stores::{ImmutableStore, MutableStore},
};
Expand Down Expand Up @@ -65,18 +66,23 @@ pub async fn page_handler<M: MutableStore, T: TranslationsManager>(
translations_manager: &translations_manager,
},
template,
false,
)
.await;
match page_data {
Ok(page_data) => {
let partial_page_data = PageDataPartial {
state: page_data.state,
head: page_data.head,
};
let mut http_res = Response::builder().status(200);
// http_res.content_type("text/html");
// Generate and add HTTP headers
for (key, val) in template.get_headers(page_data.state.clone()) {
for (key, val) in template.get_headers(partial_page_data.state.clone()) {
http_res = http_res.header(key.unwrap(), val);
}

let page_data_str = serde_json::to_string(&page_data).unwrap();
let page_data_str = serde_json::to_string(&partial_page_data).unwrap();
http_res.body(page_data_str).unwrap()
}
// We parse the error to return an appropriate status code
Expand Down
13 changes: 11 additions & 2 deletions packages/perseus/src/export.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::errors::*;
use crate::i18n::{Locales, TranslationsManager};
use crate::page_data::PageDataPartial;
use crate::server::{get_render_cfg, HtmlShell};
use crate::stores::ImmutableStore;
use crate::template::TemplateMap;
Expand Down Expand Up @@ -216,7 +217,11 @@ pub async fn export_path(

// Serialize the page data to JSON and write it as a partial (fetched by the app
// shell for subsequent loads)
let partial = serde_json::to_string(&page_data).unwrap();
let partial_page_data = PageDataPartial {
state: page_data.state,
head: page_data.head,
};
let partial = serde_json::to_string(&partial_page_data).unwrap();
immutable_store
.write(
&format!("exported/.perseus/page/{}/{}.json", locale, &path),
Expand Down Expand Up @@ -246,7 +251,11 @@ pub async fn export_path(

// Serialize the page data to JSON and write it as a partial (fetched by the app
// shell for subsequent loads)
let partial = serde_json::to_string(&page_data).unwrap();
let partial_page_data = PageDataPartial {
state: page_data.state,
head: page_data.head,
};
let partial = serde_json::to_string(&partial_page_data).unwrap();
immutable_store
.write(
&format!("exported/.perseus/page/{}/{}.json", locales.default, &path),
Expand Down
1 change: 1 addition & 0 deletions packages/perseus/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ pub use client::{run_client, ClientReturn};
/// TODO
#[cfg(not(target_arch = "wasm32"))]
pub mod internal {
pub use crate::page_data::*;
pub use crate::{build::*, export::*};
}
/// Internal utilities for logging. These are just re-exports so that users
Expand Down
15 changes: 15 additions & 0 deletions packages/perseus/src/page_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,18 @@ pub struct PageData {
/// The string to interpolate into the document's `<head>`.
pub head: String,
}

/// A version of [`PageData`] that doesn't contain the HTML content of the page.
/// This is designed for being to sent to the client as part of a subsequent
/// load, since the browser can render the HTML content of a page on its own.
///
/// Note that this still contains the `<head>`.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct PageDataPartial {
/// The state for hydration. This is kept as a string for ease of typing.
/// Some pages may not need state or generate it in another way, so this
/// might be `None`.
pub state: Option<String>,
/// The string to interpolate into the document's `<head>`.
pub head: String,
}
4 changes: 2 additions & 2 deletions packages/perseus/src/router/get_subsequent_view.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::error_pages::ErrorPages;
use crate::errors::*;
use crate::i18n::ClientTranslationsManager;
use crate::page_data::PageData;
use crate::page_data::PageDataPartial;
use crate::router::{get_global_state, RouteVerdict, RouterLoadState, RouterState};
use crate::template::{PageProps, Template, TemplateNodeType};
use crate::utils::checkpoint;
Expand Down Expand Up @@ -97,7 +97,7 @@ pub(crate) async fn get_subsequent_view(
Ok(page_data_str_opt) => match page_data_str_opt {
Some(page_data_str) => {
// All good, deserialize the page data
let page_data = serde_json::from_str::<PageData>(&page_data_str);
let page_data = serde_json::from_str::<PageDataPartial>(&page_data_str);
match page_data {
Ok(page_data) => {
// Interpolate the metadata directly into the document's `<head>`
Expand Down
Loading

0 comments on commit e1c9ad3

Please sign in to comment.