Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Server-side Rendering (without hydration) #2335

Merged
merged 31 commits into from
Jan 12, 2022
Merged
Changes from 1 commit
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
65af3a2
Basic render to html implementation.
futursolo Jan 6, 2022
667796d
Remove HtmlWriter.
futursolo Jan 6, 2022
820d3ac
Escape html content.
futursolo Jan 6, 2022
1b2d823
Add non-suspense tests.
futursolo Jan 6, 2022
5b3f5e3
Add Suspense tests.
futursolo Jan 6, 2022
c7c9d2d
Gated "ssr" feature.
futursolo Jan 6, 2022
2bab219
Add example.
futursolo Jan 6, 2022
90deefc
merge master into ssr
futursolo Jan 6, 2022
482865e
Fix tests.
futursolo Jan 6, 2022
10f4ac4
Fix docs.
futursolo Jan 6, 2022
77b48c0
Fix heading size.
futursolo Jan 6, 2022
476300a
Remove the unused YewRenderer.
futursolo Jan 6, 2022
95e1d39
Remove extra comment.
futursolo Jan 6, 2022
655afd4
unify naming.
futursolo Jan 6, 2022
26bebb5
Update docs.
futursolo Jan 7, 2022
61f44d1
Update docs.
futursolo Jan 7, 2022
826b431
Update docs.
futursolo Jan 7, 2022
ed138b1
Isolate spawn_local.
futursolo Jan 7, 2022
bd26db4
Add doc flags.
futursolo Jan 7, 2022
f003aab
Add ssr feature to docs.
futursolo Jan 7, 2022
e56fcef
Move ServerRenderer into their own file.
futursolo Jan 7, 2022
ebe8c68
Fix docs.
futursolo Jan 7, 2022
10d7295
Update features and docs.
futursolo Jan 8, 2022
8635d63
Fix example.
futursolo Jan 8, 2022
3599240
Adjust comment position.
futursolo Jan 8, 2022
39a3d06
Fix effects being wrongly called when a component is suspended.
futursolo Jan 9, 2022
49566a0
Fix clippy.
futursolo Jan 9, 2022
17fd3b9
Uuid & no double boxing.
futursolo Jan 11, 2022
c67de39
merge master into ssr
futursolo Jan 11, 2022
0f24ae6
Merge branch 'master' into ssr
futursolo Jan 11, 2022
3567689
Merge branch 'master' into fursolo-ssr
ranile Jan 12, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Isolate spawn_local.
futursolo committed Jan 7, 2022
commit ed138b172cfd4e0a47ba6c8b9348a61605762f02
13 changes: 12 additions & 1 deletion packages/yew/Cargo.toml
Original file line number Diff line number Diff line change
@@ -23,7 +23,6 @@ indexmap = { version = "1", features = ["std"] }
js-sys = "0.3"
slab = "0.4"
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
yew-macro = { version = "^0.19.0", path = "../yew-macro" }
thiserror = "1.0"

@@ -63,16 +62,28 @@ features = [
"Window",
]

[target.'cfg(target_arch = "wasm32")'.dependencies]
# we move it here so no promise-based spawn_local can present for
# non-wasm32 targets.
wasm-bindgen-futures = "0.4"

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
tokio = { version = "1.15.0", features = ["rt"], optional = true }

[dev-dependencies]
easybench-wasm = "0.2"
wasm-bindgen-test = "0.3"
gloo = { version = "0.4", features = ["futures"] }
wasm-bindgen-futures = "0.4"

[features]
doc_test = []
wasm_test = []
wasm_bench = []
ssr = ["futures", "html-escape"]
# we enable tokio by default so spawn_local is available on non-wasm targets
# by default as well.
default = ["tokio"]

[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
tokio = { version = "1.15.0", features = ["full"] }
123 changes: 66 additions & 57 deletions packages/yew/src/html/component/scope.rs
Original file line number Diff line number Diff line change
@@ -15,11 +15,9 @@ use crate::virtual_dom::{insert_node, VNode};
use gloo_utils::document;
use std::any::{Any, TypeId};
use std::cell::{Ref, RefCell};
use std::future::Future;
use std::ops::Deref;
use std::rc::Rc;
use std::{fmt, iter};
use wasm_bindgen_futures::spawn_local;
use web_sys::{Element, Node};

/// Untyped scope used for accessing parent scope
@@ -350,61 +348,6 @@ impl<COMP: BaseComponent> Scope<COMP> {
};
closure.into()
}
/// This method creates a [`Callback`] which returns a Future which
/// returns a message to be sent back to the component's event
/// loop.
///
/// # Panics
/// If the future panics, then the promise will not resolve, and
/// will leak.
pub fn callback_future<FN, FU, IN, M>(&self, function: FN) -> Callback<IN>
where
M: Into<COMP::Message>,
FU: Future<Output = M> + 'static,
FN: Fn(IN) -> FU + 'static,
{
let link = self.clone();

let closure = move |input: IN| {
let future: FU = function(input);
link.send_future(future);
};

closure.into()
}

/// This method processes a Future that returns a message and sends it back to the component's
/// loop.
///
/// # Panics
/// If the future panics, then the promise will not resolve, and will leak.
pub fn send_future<F, M>(&self, future: F)
where
M: Into<COMP::Message>,
F: Future<Output = M> + 'static,
{
let link = self.clone();
let js_future = async move {
let message: COMP::Message = future.await.into();
link.send_message(message);
};
spawn_local(js_future);
}

/// Registers a Future that resolves to multiple messages.
/// # Panics
/// If the future panics, then the promise will not resolve, and will leak.
pub fn send_future_batch<F>(&self, future: F)
where
F: Future<Output = Vec<COMP::Message>> + 'static,
{
let link = self.clone();
let js_future = async move {
let messages: Vec<COMP::Message> = future.await;
link.send_message_batch(messages);
};
spawn_local(js_future);
}

/// Accesses a value provided by a parent `ContextProvider` component of the
/// same type.
@@ -457,6 +400,72 @@ mod feat_ssr {
}
}

#[cfg(any(target_arch = "wasm32", feature = "tokio"))]
mod feat_io {
use std::future::Future;

use super::*;
use crate::io_coop::spawn_local;

impl<COMP: BaseComponent> Scope<COMP> {
/// This method creates a [`Callback`] which returns a Future which
/// returns a message to be sent back to the component's event
/// loop.
///
/// # Panics
/// If the future panics, then the promise will not resolve, and
/// will leak.
pub fn callback_future<FN, FU, IN, M>(&self, function: FN) -> Callback<IN>
where
M: Into<COMP::Message>,
FU: Future<Output = M> + 'static,
FN: Fn(IN) -> FU + 'static,
{
let link = self.clone();

let closure = move |input: IN| {
let future: FU = function(input);
link.send_future(future);
};

closure.into()
}

/// This method processes a Future that returns a message and sends it back to the component's
/// loop.
///
/// # Panics
/// If the future panics, then the promise will not resolve, and will leak.
pub fn send_future<F, M>(&self, future: F)
where
M: Into<COMP::Message>,
F: Future<Output = M> + 'static,
{
let link = self.clone();
let js_future = async move {
let message: COMP::Message = future.await.into();
link.send_message(message);
};
spawn_local(js_future);
}

/// Registers a Future that resolves to multiple messages.
/// # Panics
/// If the future panics, then the promise will not resolve, and will leak.
pub fn send_future_batch<F>(&self, future: F)
where
F: Future<Output = Vec<COMP::Message>> + 'static,
{
let link = self.clone();
let js_future = async move {
let messages: Vec<COMP::Message> = future.await;
link.send_message_batch(messages);
};
spawn_local(js_future);
}
}
}

/// Defines a message type that can be sent to a component.
/// Used for the return value of closure given to [Scope::batch_callback](struct.Scope.html#method.batch_callback).
pub trait SendAsMessage<COMP: BaseComponent> {
27 changes: 27 additions & 0 deletions packages/yew/src/io_coop.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//! module that provides io compatibility over browser tasks and other async io tasks (e.g.: tokio)
#[cfg(target_arch = "wasm32")]
mod io_wasm_bindgen {
pub use wasm_bindgen_futures::spawn_local;
}

#[cfg(target_arch = "wasm32")]
pub(crate) use io_wasm_bindgen::*;

#[cfg(all(not(target_arch = "wasm32"), feature = "tokio"))]
mod io_tokio {
use std::future::Future;

// spawn_local in tokio is more powerful, but we need to adjust the function signature to match
// wasm_bindgen_futures.
#[inline(always)]
pub(crate) fn spawn_local<F>(f: F)
where
F: Future<Output = ()> + 'static,
{
tokio::task::spawn_local(f);
}
}

#[cfg(all(not(target_arch = "wasm32"), feature = "tokio"))]
pub(crate) use io_tokio::*;
1 change: 1 addition & 0 deletions packages/yew/src/lib.rs
Original file line number Diff line number Diff line change
@@ -260,6 +260,7 @@ pub mod callback;
pub mod context;
pub mod functional;
pub mod html;
mod io_coop;
pub mod scheduler;
mod sealed;
pub mod suspense;
33 changes: 20 additions & 13 deletions packages/yew/src/suspense/suspension.rs
Original file line number Diff line number Diff line change
@@ -6,7 +6,6 @@ use std::sync::atomic::{AtomicBool, Ordering};
use std::task::{Context, Poll};

use thiserror::Error;
use wasm_bindgen_futures::spawn_local;

use crate::Callback;

@@ -52,18 +51,6 @@ impl Suspension {
(self_.clone(), SuspensionHandle { inner: self_ })
}

/// Creates a Suspension that resumes when the [`Future`] resolves.
pub fn from_future(f: impl Future<Output = ()> + 'static) -> Self {
let (self_, handle) = Self::new();

spawn_local(async move {
f.await;
handle.resume();
});

self_
}

/// Returns `true` if the current suspension is already resumed.
pub fn resumed(&self) -> bool {
self.resumed.load(Ordering::Relaxed)
@@ -138,3 +125,23 @@ impl Drop for SuspensionHandle {
self.inner.resume_by_ref();
}
}

#[cfg(any(target_arch = "wasm32", feature = "tokio"))]
mod feat_io {
use super::*;
use crate::io_coop::spawn_local;

impl Suspension {
/// Creates a Suspension that resumes when the [`Future`] resolves.
pub fn from_future(f: impl Future<Output = ()> + 'static) -> Self {
let (self_, handle) = Self::new();

spawn_local(async move {
f.await;
handle.resume();
});

self_
}
}
}