Skip to content

Commit e1c9ad3

Browse files
committed
perf: removed unnecessary content pre-rendering from server render process
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.
1 parent 0c7c89c commit e1c9ad3

File tree

11 files changed

+148
-48
lines changed

11 files changed

+148
-48
lines changed

packages/perseus-actix-web/src/initial_load.rs

+1
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ pub async fn initial_load<M: MutableStore, T: TranslationsManager>(
9191
translations_manager: translations_manager.get_ref(),
9292
},
9393
template,
94+
true, // This is an initial load, so we do want the content rendered/fetched
9495
)
9596
.await;
9697
let page_data = match page_data {

packages/perseus-actix-web/src/page_data.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use fmterr::fmt_err;
44
use perseus::{
55
errors::err_to_status_code,
66
i18n::TranslationsManager,
7+
internal::PageDataPartial,
78
server::{get_page_for_template, GetPageProps, ServerOptions},
89
stores::{ImmutableStore, MutableStore},
910
};
@@ -68,18 +69,23 @@ pub async fn page_data<M: MutableStore, T: TranslationsManager>(
6869
translations_manager: translations_manager.get_ref(),
6970
},
7071
template,
72+
false, // For subsequent loads, we don't want to render content (the client can do it)
7173
)
7274
.await;
7375
match page_data {
7476
Ok(page_data) => {
77+
let partial_page_data = PageDataPartial {
78+
state: page_data.state,
79+
head: page_data.head,
80+
};
7581
let mut http_res = HttpResponse::Ok();
7682
http_res.content_type("text/html");
7783
// Generate and add HTTP headers
78-
for (key, val) in template.get_headers(page_data.state.clone()) {
84+
for (key, val) in template.get_headers(partial_page_data.state.clone()) {
7985
http_res.insert_header((key.unwrap(), val));
8086
}
8187

82-
http_res.body(serde_json::to_string(&page_data).unwrap())
88+
http_res.body(serde_json::to_string(&partial_page_data).unwrap())
8389
}
8490
// We parse the error to return an appropriate status code
8591
Err(err) => {

packages/perseus-axum/src/initial_load.rs

+1
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ pub async fn initial_load_handler<M: MutableStore, T: TranslationsManager>(
8686
translations_manager: &translations_manager,
8787
},
8888
template,
89+
true,
8990
)
9091
.await;
9192
let page_data = match page_data {

packages/perseus-axum/src/page_data.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use fmterr::fmt_err;
77
use perseus::{
88
errors::err_to_status_code,
99
i18n::TranslationsManager,
10+
internal::PageDataPartial,
1011
server::{get_page_for_template, GetPageProps, ServerOptions},
1112
stores::{ImmutableStore, MutableStore},
1213
Request,
@@ -85,18 +86,23 @@ pub async fn page_handler<M: MutableStore, T: TranslationsManager>(
8586
translations_manager: &translations_manager,
8687
},
8788
template,
89+
false,
8890
)
8991
.await;
9092
match page_data {
9193
Ok(page_data) => {
94+
let partial_page_data = PageDataPartial {
95+
state: page_data.state,
96+
head: page_data.head,
97+
};
9298
// http_res.content_type("text/html");
9399
// Generate and add HTTP headers
94100
let mut header_map = HeaderMap::new();
95-
for (key, val) in template.get_headers(page_data.state.clone()) {
101+
for (key, val) in template.get_headers(partial_page_data.state.clone()) {
96102
header_map.insert(key.unwrap(), val);
97103
}
98104

99-
let page_data_str = serde_json::to_string(&page_data).unwrap();
105+
let page_data_str = serde_json::to_string(&partial_page_data).unwrap();
100106

101107
(StatusCode::OK, header_map, page_data_str)
102108
}

packages/perseus-warp/src/initial_load.rs

+1
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ pub async fn initial_load_handler<M: MutableStore, T: TranslationsManager>(
7777
translations_manager: &translations_manager,
7878
},
7979
template,
80+
true,
8081
)
8182
.await;
8283
let page_data = match page_data {

packages/perseus-warp/src/page_data.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use fmterr::fmt_err;
22
use perseus::{
33
errors::err_to_status_code,
44
i18n::TranslationsManager,
5+
internal::PageDataPartial,
56
server::{get_page_for_template, GetPageProps, ServerOptions},
67
stores::{ImmutableStore, MutableStore},
78
};
@@ -65,18 +66,23 @@ pub async fn page_handler<M: MutableStore, T: TranslationsManager>(
6566
translations_manager: &translations_manager,
6667
},
6768
template,
69+
false,
6870
)
6971
.await;
7072
match page_data {
7173
Ok(page_data) => {
74+
let partial_page_data = PageDataPartial {
75+
state: page_data.state,
76+
head: page_data.head,
77+
};
7278
let mut http_res = Response::builder().status(200);
7379
// http_res.content_type("text/html");
7480
// Generate and add HTTP headers
75-
for (key, val) in template.get_headers(page_data.state.clone()) {
81+
for (key, val) in template.get_headers(partial_page_data.state.clone()) {
7682
http_res = http_res.header(key.unwrap(), val);
7783
}
7884

79-
let page_data_str = serde_json::to_string(&page_data).unwrap();
85+
let page_data_str = serde_json::to_string(&partial_page_data).unwrap();
8086
http_res.body(page_data_str).unwrap()
8187
}
8288
// We parse the error to return an appropriate status code

packages/perseus/src/export.rs

+11-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::errors::*;
22
use crate::i18n::{Locales, TranslationsManager};
3+
use crate::page_data::PageDataPartial;
34
use crate::server::{get_render_cfg, HtmlShell};
45
use crate::stores::ImmutableStore;
56
use crate::template::TemplateMap;
@@ -216,7 +217,11 @@ pub async fn export_path(
216217

217218
// Serialize the page data to JSON and write it as a partial (fetched by the app
218219
// shell for subsequent loads)
219-
let partial = serde_json::to_string(&page_data).unwrap();
220+
let partial_page_data = PageDataPartial {
221+
state: page_data.state,
222+
head: page_data.head,
223+
};
224+
let partial = serde_json::to_string(&partial_page_data).unwrap();
220225
immutable_store
221226
.write(
222227
&format!("exported/.perseus/page/{}/{}.json", locale, &path),
@@ -246,7 +251,11 @@ pub async fn export_path(
246251

247252
// Serialize the page data to JSON and write it as a partial (fetched by the app
248253
// shell for subsequent loads)
249-
let partial = serde_json::to_string(&page_data).unwrap();
254+
let partial_page_data = PageDataPartial {
255+
state: page_data.state,
256+
head: page_data.head,
257+
};
258+
let partial = serde_json::to_string(&partial_page_data).unwrap();
250259
immutable_store
251260
.write(
252261
&format!("exported/.perseus/page/{}/{}.json", locales.default, &path),

packages/perseus/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ pub use client::{run_client, ClientReturn};
101101
/// TODO
102102
#[cfg(not(target_arch = "wasm32"))]
103103
pub mod internal {
104+
pub use crate::page_data::*;
104105
pub use crate::{build::*, export::*};
105106
}
106107
/// Internal utilities for logging. These are just re-exports so that users

packages/perseus/src/page_data.rs

+15
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,18 @@ pub struct PageData {
1212
/// The string to interpolate into the document's `<head>`.
1313
pub head: String,
1414
}
15+
16+
/// A version of [`PageData`] that doesn't contain the HTML content of the page.
17+
/// This is designed for being to sent to the client as part of a subsequent
18+
/// load, since the browser can render the HTML content of a page on its own.
19+
///
20+
/// Note that this still contains the `<head>`.
21+
#[derive(Serialize, Deserialize, Debug, Clone)]
22+
pub struct PageDataPartial {
23+
/// The state for hydration. This is kept as a string for ease of typing.
24+
/// Some pages may not need state or generate it in another way, so this
25+
/// might be `None`.
26+
pub state: Option<String>,
27+
/// The string to interpolate into the document's `<head>`.
28+
pub head: String,
29+
}

packages/perseus/src/router/get_subsequent_view.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::error_pages::ErrorPages;
22
use crate::errors::*;
33
use crate::i18n::ClientTranslationsManager;
4-
use crate::page_data::PageData;
4+
use crate::page_data::PageDataPartial;
55
use crate::router::{get_global_state, RouteVerdict, RouterLoadState, RouterState};
66
use crate::template::{PageProps, Template, TemplateNodeType};
77
use crate::utils::checkpoint;
@@ -97,7 +97,7 @@ pub(crate) async fn get_subsequent_view(
9797
Ok(page_data_str_opt) => match page_data_str_opt {
9898
Some(page_data_str) => {
9999
// All good, deserialize the page data
100-
let page_data = serde_json::from_str::<PageData>(&page_data_str);
100+
let page_data = serde_json::from_str::<PageDataPartial>(&page_data_str);
101101
match page_data {
102102
Ok(page_data) => {
103103
// Interpolate the metadata directly into the document's `<head>`

0 commit comments

Comments
 (0)