mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-10 14:44:12 +00:00
create server_cached function
This commit is contained in:
parent
d885846589
commit
de72d85391
8 changed files with 182 additions and 14 deletions
|
@ -16,10 +16,32 @@ struct AppProps {
|
|||
}
|
||||
|
||||
fn app(cx: Scope<AppProps>) -> Element {
|
||||
let state1 = server_cached(|| {
|
||||
#[cfg(not(feature = "ssr"))]
|
||||
panic!();
|
||||
12345
|
||||
});
|
||||
assert_eq!(state1, 12345);
|
||||
let state2 = server_cached(|| {
|
||||
#[cfg(not(feature = "ssr"))]
|
||||
panic!();
|
||||
123456
|
||||
});
|
||||
assert_eq!(state2, 123456);
|
||||
let state3 = server_cached(|| {
|
||||
#[cfg(not(feature = "ssr"))]
|
||||
panic!();
|
||||
1234567
|
||||
});
|
||||
assert_eq!(state3, 1234567);
|
||||
|
||||
let mut count = use_state(cx, || cx.props.count);
|
||||
let text = use_state(cx, || "...".to_string());
|
||||
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
"Server state: {state1}, {state2}, {state3}"
|
||||
}
|
||||
h1 { "High-Five counter: {count}" }
|
||||
button { onclick: move |_| count += 1, "Up high!" }
|
||||
button { onclick: move |_| count -= 1, "Down low!" }
|
||||
|
@ -42,7 +64,7 @@ fn app(cx: Scope<AppProps>) -> Element {
|
|||
|
||||
#[server(PostServerData)]
|
||||
async fn post_server_data(data: String) -> Result<(), ServerFnError> {
|
||||
let axum::extract::Host(host): axum::extract::Host = extract()?;
|
||||
let axum::extract::Host(host): axum::extract::Host = extract().await?;
|
||||
println!("Server received: {}", data);
|
||||
println!("{:?}", host);
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@ use serde::de::DeserializeOwned;
|
|||
use base64::engine::general_purpose::STANDARD;
|
||||
use base64::Engine;
|
||||
|
||||
use super::HTMLDataCursor;
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) fn serde_from_bytes<T: DeserializeOwned>(string: &[u8]) -> Option<T> {
|
||||
let decompressed = STANDARD.decode(string).ok()?;
|
||||
|
@ -10,6 +12,29 @@ pub(crate) fn serde_from_bytes<T: DeserializeOwned>(string: &[u8]) -> Option<T>
|
|||
postcard::from_bytes(&decompressed).ok()
|
||||
}
|
||||
|
||||
static SERVER_DATA: once_cell::sync::Lazy<Option<HTMLDataCursor>> =
|
||||
once_cell::sync::Lazy::new(|| {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
let attribute = web_sys::window()?
|
||||
.document()?
|
||||
.get_element_by_id("dioxus-storage-data")?
|
||||
.get_attribute("data-serialized")?;
|
||||
|
||||
let data: super::HTMLData = serde_from_bytes(attribute.as_bytes())?;
|
||||
|
||||
Some(data.cursor())
|
||||
}
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
pub(crate) fn take_server_data<T: DeserializeOwned>() -> Option<T> {
|
||||
SERVER_DATA.as_ref()?.take()
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "ssr"))]
|
||||
/// Get the props from the document. This is only available in the browser.
|
||||
///
|
||||
|
@ -23,7 +48,7 @@ pub fn get_root_props_from_document<T: DeserializeOwned>() -> Option<T> {
|
|||
{
|
||||
let attribute = web_sys::window()?
|
||||
.document()?
|
||||
.get_element_by_id("dioxus-storage")?
|
||||
.get_element_by_id("dioxus-storage-props")?
|
||||
.get_attribute("data-serialized")?;
|
||||
|
||||
serde_from_bytes(attribute.as_bytes())
|
|
@ -1,6 +1,46 @@
|
|||
pub(crate) mod deserialize_props;
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
|
||||
pub(crate) mod serialize_props;
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
|
||||
pub(crate) mod deserialize;
|
||||
|
||||
pub(crate) mod serialize;
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize, Default)]
|
||||
pub(crate) struct HTMLData {
|
||||
pub data: Vec<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl HTMLData {
|
||||
pub(crate) fn push<T: Serialize>(&mut self, value: &T) {
|
||||
let serialized = postcard::to_allocvec(value).unwrap();
|
||||
self.data.push(serialized);
|
||||
}
|
||||
|
||||
pub(crate) fn cursor(self) -> HTMLDataCursor {
|
||||
HTMLDataCursor {
|
||||
data: self.data,
|
||||
index: AtomicUsize::new(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct HTMLDataCursor {
|
||||
data: Vec<Vec<u8>>,
|
||||
index: AtomicUsize,
|
||||
}
|
||||
|
||||
impl HTMLDataCursor {
|
||||
pub fn take<T: DeserializeOwned>(&self) -> Option<T> {
|
||||
let current = self.index.load(std::sync::atomic::Ordering::SeqCst);
|
||||
if current >= self.data.len() {
|
||||
return None;
|
||||
}
|
||||
let mut cursor = &self.data[current];
|
||||
self.index.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
|
||||
Some(postcard::from_bytes(&mut cursor).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialized_and_deserializes() {
|
||||
|
@ -37,7 +77,7 @@ fn serialized_and_deserializes() {
|
|||
};
|
||||
y
|
||||
];
|
||||
serialize_props::serde_to_writable(&data, &mut as_string).unwrap();
|
||||
serialize::serde_to_writable(&data, &mut as_string).unwrap();
|
||||
|
||||
println!("{:?}", as_string);
|
||||
println!(
|
||||
|
@ -47,7 +87,7 @@ fn serialized_and_deserializes() {
|
|||
println!("serialized size: {}", to_allocvec(&data).unwrap().len());
|
||||
println!("compressed size: {}", as_string.len());
|
||||
|
||||
let decoded: Vec<Data> = deserialize_props::serde_from_bytes(&as_string).unwrap();
|
||||
let decoded: Vec<Data> = deserialize::serde_from_bytes(&as_string).unwrap();
|
||||
assert_eq!(data, decoded);
|
||||
}
|
||||
}
|
|
@ -15,12 +15,26 @@ pub(crate) fn serde_to_writable<T: Serialize>(
|
|||
|
||||
#[cfg(feature = "ssr")]
|
||||
/// Encode data into a element. This is inteded to be used in the server to send data to the client.
|
||||
pub(crate) fn encode_in_element<T: Serialize>(
|
||||
data: T,
|
||||
pub(crate) fn encode_props_in_element<T: Serialize>(
|
||||
data: &T,
|
||||
write_to: &mut impl std::io::Write,
|
||||
) -> std::io::Result<()> {
|
||||
write_to
|
||||
.write_all(r#"<meta hidden="true" id="dioxus-storage" data-serialized=""#.as_bytes())?;
|
||||
write_to.write_all(
|
||||
r#"<meta hidden="true" id="dioxus-storage-props" data-serialized=""#.as_bytes(),
|
||||
)?;
|
||||
serde_to_writable(data, write_to)?;
|
||||
write_to.write_all(r#"" />"#.as_bytes())
|
||||
}
|
||||
|
||||
#[cfg(feature = "ssr")]
|
||||
/// Encode data into a element. This is inteded to be used in the server to send data to the client.
|
||||
pub(crate) fn encode_in_element(
|
||||
data: &super::HTMLData,
|
||||
write_to: &mut impl std::io::Write,
|
||||
) -> std::io::Result<()> {
|
||||
write_to.write_all(
|
||||
r#"<meta hidden="true" id="dioxus-storage-data" data-serialized=""#.as_bytes(),
|
||||
)?;
|
||||
serde_to_writable(&data, write_to)?;
|
||||
write_to.write_all(r#"" />"#.as_bytes())
|
||||
}
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
pub use once_cell;
|
||||
|
||||
mod props_html;
|
||||
mod html_storage;
|
||||
|
||||
#[cfg(feature = "router")]
|
||||
pub mod router;
|
||||
|
@ -26,6 +26,7 @@ mod serve_config;
|
|||
#[cfg(feature = "ssr")]
|
||||
mod server_context;
|
||||
mod server_fn;
|
||||
mod use_server;
|
||||
|
||||
/// A prelude of commonly used items in dioxus-fullstack.
|
||||
pub mod prelude {
|
||||
|
@ -36,7 +37,7 @@ pub mod prelude {
|
|||
#[cfg(feature = "warp")]
|
||||
pub use crate::adapters::warp_adapter::*;
|
||||
#[cfg(not(feature = "ssr"))]
|
||||
pub use crate::props_html::deserialize_props::get_root_props_from_document;
|
||||
pub use crate::html_storage::deserialize::get_root_props_from_document;
|
||||
#[cfg(all(feature = "ssr", feature = "router"))]
|
||||
pub use crate::render::pre_cache_static_routes_with_props;
|
||||
#[cfg(feature = "ssr")]
|
||||
|
@ -52,9 +53,12 @@ pub mod prelude {
|
|||
pub use crate::server_fn::DioxusServerFn;
|
||||
#[cfg(feature = "ssr")]
|
||||
pub use crate::server_fn::{ServerFnMiddleware, ServerFnTraitObj, ServerFunction};
|
||||
use crate::use_server;
|
||||
pub use crate::{launch, launch_router};
|
||||
pub use dioxus_server_macro::*;
|
||||
#[cfg(feature = "ssr")]
|
||||
pub use dioxus_ssr::incremental::IncrementalRendererConfig;
|
||||
pub use server_fn::{self, ServerFn as _, ServerFnError};
|
||||
|
||||
pub use use_server::server_cached;
|
||||
}
|
||||
|
|
|
@ -27,7 +27,10 @@ impl SsrRendererPool {
|
|||
to: &mut WriteBuffer,
|
||||
server_context: &DioxusServerContext,
|
||||
) -> Result<RenderFreshness, dioxus_ssr::incremental::IncrementalRendererError> {
|
||||
let wrapper = FullstackRenderer { cfg };
|
||||
let wrapper = FullstackRenderer {
|
||||
cfg,
|
||||
server_context: server_context.clone(),
|
||||
};
|
||||
match self {
|
||||
Self::Renderer(pool) => {
|
||||
let server_context = Box::new(server_context.clone());
|
||||
|
@ -126,6 +129,7 @@ impl SSRState {
|
|||
|
||||
struct FullstackRenderer<'a, P: Clone + Send + Sync + 'static> {
|
||||
cfg: &'a ServeConfig<P>,
|
||||
server_context: DioxusServerContext,
|
||||
}
|
||||
|
||||
impl<'a, P: Clone + Serialize + Send + Sync + 'static> dioxus_ssr::incremental::WrapBody
|
||||
|
@ -147,7 +151,29 @@ impl<'a, P: Clone + Serialize + Send + Sync + 'static> dioxus_ssr::incremental::
|
|||
to: &mut R,
|
||||
) -> Result<(), dioxus_ssr::incremental::IncrementalRendererError> {
|
||||
// serialize the props
|
||||
crate::props_html::serialize_props::encode_in_element(&self.cfg.props, to)?;
|
||||
crate::html_storage::serialize::encode_props_in_element(&self.cfg.props, to)?;
|
||||
// serialize the server state
|
||||
crate::html_storage::serialize::encode_in_element(
|
||||
&*self.server_context.html_data().map_err(|err| {
|
||||
dioxus_ssr::incremental::IncrementalRendererError::Other(Box::new({
|
||||
#[derive(Debug)]
|
||||
struct HTMLDataReadError;
|
||||
|
||||
impl std::fmt::Display for HTMLDataReadError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(
|
||||
"Failed to read the server data to serialize it into the HTML",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for HTMLDataReadError {}
|
||||
|
||||
HTMLDataReadError
|
||||
}))
|
||||
})?,
|
||||
to,
|
||||
)?;
|
||||
|
||||
#[cfg(all(debug_assertions, feature = "hot-reload"))]
|
||||
{
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use crate::html_storage::HTMLData;
|
||||
pub use server_fn_impl::*;
|
||||
use std::sync::Arc;
|
||||
use std::sync::RwLock;
|
||||
|
@ -13,6 +14,7 @@ pub struct DioxusServerContext {
|
|||
>,
|
||||
response_parts: std::sync::Arc<std::sync::RwLock<http::response::Parts>>,
|
||||
pub(crate) parts: Arc<RwLock<http::request::Parts>>,
|
||||
html_data: Arc<RwLock<HTMLData>>,
|
||||
}
|
||||
|
||||
#[allow(clippy::derivable_impls)]
|
||||
|
@ -24,6 +26,7 @@ impl Default for DioxusServerContext {
|
|||
http::response::Response::new(()).into_parts().0,
|
||||
)),
|
||||
parts: std::sync::Arc::new(RwLock::new(http::request::Request::new(()).into_parts().0)),
|
||||
html_data: Arc::new(RwLock::new(HTMLData::default())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -45,6 +48,7 @@ mod server_fn_impl {
|
|||
response_parts: std::sync::Arc::new(RwLock::new(
|
||||
http::response::Response::new(()).into_parts().0,
|
||||
)),
|
||||
html_data: Arc::new(RwLock::new(HTMLData::default())),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -100,6 +104,21 @@ mod server_fn_impl {
|
|||
) -> Result<T, R> {
|
||||
T::from_request(self).await
|
||||
}
|
||||
|
||||
/// Insert some data into the html data store
|
||||
pub(crate) async fn push_html_data<T: serde::Serialize>(
|
||||
&self,
|
||||
value: &T,
|
||||
) -> Result<(), PoisonError<RwLockWriteGuard<'_, HTMLData>>> {
|
||||
self.html_data.write().map(|mut map| {
|
||||
map.push(value);
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the html data store
|
||||
pub(crate) fn html_data(&self) -> LockResult<RwLockReadGuard<'_, HTMLData>> {
|
||||
self.html_data.read()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
18
packages/fullstack/src/use_server/mod.rs
Normal file
18
packages/fullstack/src/use_server/mod.rs
Normal file
|
@ -0,0 +1,18 @@
|
|||
use serde::{de::DeserializeOwned, Serialize};
|
||||
|
||||
/// TODO: Document this
|
||||
pub fn server_cached<O: 'static + Serialize + DeserializeOwned>(server_fn: impl Fn() -> O) -> O {
|
||||
#[cfg(feature = "ssr")]
|
||||
{
|
||||
let data =
|
||||
crate::html_storage::deserialize::take_server_data().unwrap_or_else(|| server_fn());
|
||||
let sc = crate::prelude::server_context();
|
||||
sc.push_html_data(&data);
|
||||
data
|
||||
}
|
||||
#[cfg(not(feature = "ssr"))]
|
||||
{
|
||||
let data = server_fn();
|
||||
data
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue