add fullstack section to the getting started guide
This commit is contained in:
1 parent
53c8585107
commit
4c22d5809e
7 files changed
+272
-19
No files matched your search
@@ -16,6 +16,7 @@ dioxus-native-core-macro = { path = "../../packages/native-core-macro" }
|
|||||||
dioxus-router = { path = "../../packages/router" }
|
dioxus-router = { path = "../../packages/router" }
|
||||||
dioxus-liveview = { path = "../../packages/liveview", features = ["axum"] }
|
dioxus-liveview = { path = "../../packages/liveview", features = ["axum"] }
|
||||||
dioxus-tui = { path = "../../packages/dioxus-tui" }
|
dioxus-tui = { path = "../../packages/dioxus-tui" }
|
||||||
|
dioxus-server = { path = "../../packages/server" }
|
||||||
fermi = { path = "../../packages/fermi" }
|
fermi = { path = "../../packages/fermi" }
|
||||||
shipyard = "0.6.2"
|
shipyard = "0.6.2"
|
||||||
|
|
||||||
@@ -23,5 +24,5 @@ shipyard = "0.6.2"
|
|||||||
# dioxus = { path = "../../packages/dioxus", features = ["desktop", "web", "ssr", "router", "fermi", "tui"] }
|
# dioxus = { path = "../../packages/dioxus", features = ["desktop", "web", "ssr", "router", "fermi", "tui"] }
|
||||||
serde = { version = "1.0.138", features=["derive"] }
|
serde = { version = "1.0.138", features=["derive"] }
|
||||||
reqwest = { version = "0.11.11", features = ["json"] }
|
reqwest = { version = "0.11.11", features = ["json"] }
|
||||||
tokio = { version = "1.19.2" , features=[]}
|
tokio = { version = "1.19.2", features = ["full"] }
|
||||||
axum = { version = "0.6.1", features = ["ws"] }
|
axum = { version = "0.6.1", features = ["ws"] }
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
mod basic {
|
||||||
|
// ANCHOR: basic
|
||||||
|
#![allow(non_snake_case)]
|
||||||
|
use dioxus::prelude::*;
|
||||||
|
use dioxus_server::prelude::*;
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
|
let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080));
|
||||||
|
axum::Server::bind(&addr)
|
||||||
|
.serve(
|
||||||
|
axum::Router::new()
|
||||||
|
.serve_dioxus_application("", ServeConfigBuilder::new(app, ()))
|
||||||
|
.into_make_service(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn app(cx: Scope) -> Element {
|
||||||
|
let mut count = use_state(cx, || 0);
|
||||||
|
|
||||||
|
cx.render(rsx! {
|
||||||
|
h1 { "High-Five counter: {count}" }
|
||||||
|
button { onclick: move |_| count += 1, "Up high!" }
|
||||||
|
button { onclick: move |_| count -= 1, "Down low!" }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// ANCHOR_END: basic
|
||||||
|
}
|
||||||
|
|
||||||
|
mod hydration {
|
||||||
|
// ANCHOR: hydration
|
||||||
|
#![allow(non_snake_case)]
|
||||||
|
use dioxus::prelude::*;
|
||||||
|
use dioxus_server::prelude::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
#[cfg(feature = "web")]
|
||||||
|
dioxus_web::launch_cfg(app, dioxus_web::Config::new().hydrate(true));
|
||||||
|
#[cfg(feature = "ssr")]
|
||||||
|
{
|
||||||
|
tokio::runtime::Runtime::new()
|
||||||
|
.unwrap()
|
||||||
|
.block_on(async move {
|
||||||
|
let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080));
|
||||||
|
axum::Server::bind(&addr)
|
||||||
|
.serve(
|
||||||
|
axum::Router::new()
|
||||||
|
.serve_dioxus_application("", ServeConfigBuilder::new(app, ()))
|
||||||
|
.into_make_service(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn app(cx: Scope) -> Element {
|
||||||
|
let mut count = use_state(cx, || 0);
|
||||||
|
|
||||||
|
cx.render(rsx! {
|
||||||
|
h1 { "High-Five counter: {count}" }
|
||||||
|
button { onclick: move |_| count += 1, "Up high!" }
|
||||||
|
button { onclick: move |_| count -= 1, "Down low!" }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// ANCHOR_END: hydration
|
||||||
|
}
|
||||||
|
|
||||||
|
mod server_function {
|
||||||
|
// ANCHOR: server_function
|
||||||
|
#![allow(non_snake_case)]
|
||||||
|
use dioxus::prelude::*;
|
||||||
|
use dioxus_server::prelude::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
#[cfg(feature = "web")]
|
||||||
|
dioxus_web::launch_cfg(app, dioxus_web::Config::new().hydrate(true));
|
||||||
|
#[cfg(feature = "ssr")]
|
||||||
|
{
|
||||||
|
// Register the server function before starting the server
|
||||||
|
DoubleServer::register().unwrap();
|
||||||
|
tokio::runtime::Runtime::new()
|
||||||
|
.unwrap()
|
||||||
|
.block_on(async move {
|
||||||
|
let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 8080));
|
||||||
|
axum::Server::bind(&addr)
|
||||||
|
.serve(
|
||||||
|
axum::Router::new()
|
||||||
|
// Serve Dioxus application automatically recognizes server functions and adds them to the API
|
||||||
|
.serve_dioxus_application("", ServeConfigBuilder::new(app, ()))
|
||||||
|
.into_make_service(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn app(cx: Scope) -> Element {
|
||||||
|
let mut count = use_state(cx, || 0);
|
||||||
|
|
||||||
|
cx.render(rsx! {
|
||||||
|
h1 { "High-Five counter: {count}" }
|
||||||
|
button { onclick: move |_| count += 1, "Up high!" }
|
||||||
|
button { onclick: move |_| count -= 1, "Down low!" }
|
||||||
|
button {
|
||||||
|
onclick: move |_| {
|
||||||
|
to_owned![count];
|
||||||
|
async move {
|
||||||
|
// Call the server function just like a local async function
|
||||||
|
if let Ok(new_count) = double_server(*count.current()).await {
|
||||||
|
count.set(new_count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Double"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[server(DoubleServer)]
|
||||||
|
async fn double_server(number: u32) -> Result<u32, ServerFnError> {
|
||||||
|
// Perform some expensive computation or access a database on the server
|
||||||
|
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
|
||||||
|
let result = number * 2;
|
||||||
|
println!("server calculated {result}");
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
// ANCHOR_END: server_function
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
- [Desktop](getting_started/desktop.md)
|
- [Desktop](getting_started/desktop.md)
|
||||||
- [Web](getting_started/web.md)
|
- [Web](getting_started/web.md)
|
||||||
- [Server-Side Rendering](getting_started/ssr.md)
|
- [Server-Side Rendering](getting_started/ssr.md)
|
||||||
|
- [Fullstack](getting_started/fullstack.md)
|
||||||
- [Liveview](getting_started/liveview.md)
|
- [Liveview](getting_started/liveview.md)
|
||||||
- [Terminal UI](getting_started/tui.md)
|
- [Terminal UI](getting_started/tui.md)
|
||||||
- [Mobile](getting_started/mobile.md)
|
- [Mobile](getting_started/mobile.md)
|
||||||
|
|||||||
@@ -0,0 +1,115 @@
|
|||||||
|
> This guide assumes you read the [Web](web.md) guide and installed the [Dioxus-cli](https://github.com/DioxusLabs/cli)
|
||||||
|
|
||||||
|
# Fullstack development
|
||||||
|
|
||||||
|
We can combine the `dioxus-web` renderer with the `dioxus-ssr` renderer to create a full-stack Dioxus application. By combining server-side rendering with client-side hydration we can create an application that is initially rendered on the server and then hydrates the application on the client. Server-side rendering results in a fast first paint and make our page SEO-friendly. Client-side hydration makes our page responsive once the application has fully loaded.
|
||||||
|
|
||||||
|
To help make full-stack development easier, Dioxus provides a `dioxus-server` crate that integrates with popular web frameworks with utilities for full-stack development.
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
For this guide, we're going to show how to use Dioxus with [Axum](https://docs.rs/axum/latest/axum/), but `dioxus-server` also integrates with the [Warp](https://docs.rs/warp/latest/warp/) and [Salvo](https://docs.rs/salvo/latest/salvo/) web frameworks.
|
||||||
|
|
||||||
|
Make sure you have Rust and Cargo installed, and then create a new project:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
cargo new --bin demo
|
||||||
|
cd demo
|
||||||
|
```
|
||||||
|
|
||||||
|
Add `dioxus` and `dioxus-server` as dependencies:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
cargo add dioxus
|
||||||
|
cargo add dioxus-server --features axum, ssr
|
||||||
|
```
|
||||||
|
|
||||||
|
Next, add all the Axum dependencies. This will be different if you're using a different Web Framework
|
||||||
|
|
||||||
|
```shell
|
||||||
|
cargo add tokio --features full
|
||||||
|
cargo add axum
|
||||||
|
```
|
||||||
|
|
||||||
|
Your dependencies should look roughly like this:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[dependencies]
|
||||||
|
axum = "*"
|
||||||
|
dioxus = { version = "*" }
|
||||||
|
dioxus-server = { version = "*", features = ["axum", "ssr"] }
|
||||||
|
tokio = { version = "*", features = ["full"] }
|
||||||
|
```
|
||||||
|
|
||||||
|
Now, set up your Axum app to serve the Dioxus app.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
{{#include ../../../examples/server.rs:basic}}
|
||||||
|
```
|
||||||
|
|
||||||
|
Now, run your app with `cargo run` and open `http://localhost:8080` in your browser. You should see a server-side rendered page with a counter.
|
||||||
|
|
||||||
|
## Hydration
|
||||||
|
|
||||||
|
Right now, the page is static. We can't interact with the buttons. To fix this, we can hydrate the page with `dioxus-web`.
|
||||||
|
|
||||||
|
First, modify your `Cargo.toml` to include two features, one for the server called `ssr`, and one for the client called `web`.
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[dependencies]
|
||||||
|
# Common dependancies
|
||||||
|
dioxus = { version = "*" }
|
||||||
|
dioxus-server = { version = "*" }
|
||||||
|
|
||||||
|
# Web dependancies
|
||||||
|
dioxus-web = { version = "*", features=["hydrate"], optional = true }
|
||||||
|
|
||||||
|
# Server dependancies
|
||||||
|
axum = { version = "0.6.12", optional = true }
|
||||||
|
tokio = { version = "1.27.0", features = ["full"], optional = true }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
ssr = ["axum", "tokio", "dioxus-server/axum"]
|
||||||
|
web = ["dioxus-web"]
|
||||||
|
```
|
||||||
|
|
||||||
|
Next, we need to modify our `main.rs` to use either hydrate on the client or render on the server depending on the active features.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
{{#include ../../../examples/server.rs:hydration}}
|
||||||
|
```
|
||||||
|
|
||||||
|
Now, build your client-side bundle with `dioxus build --features web` and run your server with `cargo run --features ssr`. You should see the same page as before, but now you can interact with the buttons!
|
||||||
|
|
||||||
|
## Communicating with the server
|
||||||
|
|
||||||
|
`dixous-server` provides server functions that allow you to call an automatically generated API on the server from the client as if it were a local function.
|
||||||
|
|
||||||
|
To make a server function, simply add the `#[server(YourUniqueType)]` attribute to a function. The function must:
|
||||||
|
|
||||||
|
- Be an async function
|
||||||
|
- Have arguments and a return type that both implement serialize and deserialize (with [serde](https://serde.rs/)).
|
||||||
|
- Return a `Result` with an error type of ServerFnError
|
||||||
|
|
||||||
|
You must call `register` on the type you passed into the server macro in your main function before starting your server to tell Dioxus about the server function.
|
||||||
|
|
||||||
|
Let's add a server function to our app that allows us to multiply the count by a number on the server.
|
||||||
|
|
||||||
|
First, add serde as a dependency:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
cargo add serde
|
||||||
|
```
|
||||||
|
|
||||||
|
Next, add the server function to your `main.rs`:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
{{#include ../../../examples/server.rs:server_function}}
|
||||||
|
```
|
||||||
|
|
||||||
|
Now, build your client-side bundle with `dioxus build --features web` and run your server with `cargo run --features ssr`. You should see a new button that multiplies the count by 2.
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
That's it! You've created a full-stack Dioxus app. You can find more examples of full-stack apps and information about how to integrate with other frameworks and desktop renderers in the [dioxus-server examples directory](https://github.com/DioxusLabs/dioxus/tree/master/packages/server/examples).
|
||||||
@@ -5,28 +5,35 @@
|
|||||||
3. Currently the cli only implements hot reloading for the web renderer. For TUI, desktop, and LiveView you can use the hot reload macro instead.
|
3. Currently the cli only implements hot reloading for the web renderer. For TUI, desktop, and LiveView you can use the hot reload macro instead.
|
||||||
|
|
||||||
# Web
|
# Web
|
||||||
|
|
||||||
For the web renderer, you can use the dioxus cli to serve your application with hot reloading enabled.
|
For the web renderer, you can use the dioxus cli to serve your application with hot reloading enabled.
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
Install [dioxus-cli](https://github.com/DioxusLabs/cli).
|
Install [dioxus-cli](https://github.com/DioxusLabs/cli).
|
||||||
Hot reloading is automatically enabled when using the web renderer on debug builds.
|
Hot reloading is automatically enabled when using the web renderer on debug builds.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
1. Run:
|
1. Run:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
dioxus serve --hot-reload
|
dioxus serve --hot-reload
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Change some code within a rsx or render macro
|
2. Change some code within a rsx or render macro
|
||||||
3. Open your localhost in a browser
|
3. Open your localhost in a browser
|
||||||
4. Save and watch the style change without recompiling
|
4. Save and watch the style change without recompiling
|
||||||
|
|
||||||
# Desktop/Liveview/TUI
|
# Desktop/Liveview/TUI/Server
|
||||||
|
|
||||||
For desktop, LiveView, and tui, you can place the hot reload macro at the top of your main function to enable hot reloading.
|
For desktop, LiveView, and tui, you can place the hot reload macro at the top of your main function to enable hot reloading.
|
||||||
Hot reloading is automatically enabled on debug builds.
|
Hot reloading is automatically enabled on debug builds.
|
||||||
|
|
||||||
For more information about hot reloading on native platforms and configuration options see the [dioxus-hot-reload](https://crates.io/crates/dioxus-hot-reload) crate.
|
For more information about hot reloading on native platforms and configuration options see the [dioxus-hot-reload](https://crates.io/crates/dioxus-hot-reload) crate.
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
Add the following to your main function:
|
Add the following to your main function:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
@@ -37,13 +44,17 @@ fn main() {
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
1. Run:
|
1. Run:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo run
|
cargo run
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Change some code within a rsx or render macro
|
2. Change some code within a rsx or render macro
|
||||||
3. Save and watch the style change without recompiling
|
3. Save and watch the style change without recompiling
|
||||||
|
|
||||||
# Limitations
|
# Limitations
|
||||||
|
|
||||||
1. The interpreter can only use expressions that existed on the last full recompile. If you introduce a new variable or expression to the rsx call, it will require a full recompile to capture the expression.
|
1. The interpreter can only use expressions that existed on the last full recompile. If you introduce a new variable or expression to the rsx call, it will require a full recompile to capture the expression.
|
||||||
2. Components, Iterators, and some attributes can contain arbitrary rust code and will trigger a full recompile when changed.
|
2. Components, Iterators, and some attributes can contain arbitrary rust code and will trigger a full recompile when changed.
|
||||||
@@ -1,17 +1,6 @@
|
|||||||
# Server-Side Rendering
|
# Server-Side Rendering
|
||||||
|
|
||||||
The Dioxus VirtualDom can be rendered server-side.
|
For lower-level control over the rendering process, you can use the `dioxus-ssr` crate directly. This can be useful when integrating with a web framework that `dioxus-server` does not support, or pre-rendering pages.
|
||||||
|
|
||||||
[Example: Dioxus DocSite](https://github.com/dioxusLabs/docsite)
|
|
||||||
|
|
||||||
## Multithreaded Support
|
|
||||||
|
|
||||||
The Dioxus VirtualDom, sadly, is not currently `Send`. Internally, we use quite a bit of interior mutability which is not thread-safe. This means you can't easily use Dioxus with most web frameworks like Tide, Rocket, Axum, etc.
|
|
||||||
|
|
||||||
To solve this, you'll want to spawn a VirtualDom on its own thread and communicate with it via channels.
|
|
||||||
|
|
||||||
When working with web frameworks that require `Send`, it is possible to render a VirtualDom immediately to a String – but you cannot hold the VirtualDom across an await point. For retained-state SSR (essentially LiveView), you'll need to create a pool of VirtualDoms.
|
|
||||||
|
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
@@ -21,7 +10,7 @@ Make sure you have Rust and Cargo installed, and then create a new project:
|
|||||||
|
|
||||||
```shell
|
```shell
|
||||||
cargo new --bin demo
|
cargo new --bin demo
|
||||||
cd app
|
cd demo
|
||||||
```
|
```
|
||||||
|
|
||||||
Add Dioxus and the ssr renderer as dependencies:
|
Add Dioxus and the ssr renderer as dependencies:
|
||||||
@@ -99,6 +88,8 @@ async fn app_endpoint() -> Html<String> {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
And that's it!
|
## Multithreaded Support
|
||||||
|
|
||||||
> You might notice that you cannot hold the VirtualDom across an await point. Dioxus is currently not ThreadSafe, so it _must_ remain on the thread it started. We are working on loosening this requirement.
|
The Dioxus VirtualDom, sadly, is not currently `Send`. Internally, we use quite a bit of interior mutability which is not thread-safe.
|
||||||
|
When working with web frameworks that require `Send`, it is possible to render a VirtualDom immediately to a String – but you cannot hold the VirtualDom across an await point. For retained-state SSR (essentially LiveView), you'll need to spawn a VirtualDom on its own thread and communicate with it via channels or create a pool of VirtualDoms.
|
||||||
|
You might notice that you cannot hold the VirtualDom across an await point. Because Dioxus is currently not ThreadSafe, it _must_ remain on the thread it started. We are working on loosening this requirement.
|
||||||
@@ -5,6 +5,7 @@ Build single-page applications that run in the browser with Dioxus. To run on th
|
|||||||
A build of Dioxus for the web will be roughly equivalent to the size of a React build (70kb vs 65kb) but it will load significantly faster because [WebAssembly can be compiled as it is streamed](https://hacks.mozilla.org/2018/01/making-webassembly-even-faster-firefoxs-new-streaming-and-tiering-compiler/).
|
A build of Dioxus for the web will be roughly equivalent to the size of a React build (70kb vs 65kb) but it will load significantly faster because [WebAssembly can be compiled as it is streamed](https://hacks.mozilla.org/2018/01/making-webassembly-even-faster-firefoxs-new-streaming-and-tiering-compiler/).
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
- [TodoMVC](https://github.com/DioxusLabs/example-projects/tree/master/todomvc)
|
- [TodoMVC](https://github.com/DioxusLabs/example-projects/tree/master/todomvc)
|
||||||
- [ECommerce](https://github.com/DioxusLabs/example-projects/tree/master/ecommerce-site)
|
- [ECommerce](https://github.com/DioxusLabs/example-projects/tree/master/ecommerce-site)
|
||||||
|
|
||||||
@@ -17,7 +18,7 @@ Examples:
|
|||||||
The Web is the best-supported target platform for Dioxus.
|
The Web is the best-supported target platform for Dioxus.
|
||||||
|
|
||||||
- Because your app will be compiled to WASM you have access to browser APIs through [wasm-bingen](https://rustwasm.github.io/docs/wasm-bindgen/introduction.html).
|
- Because your app will be compiled to WASM you have access to browser APIs through [wasm-bingen](https://rustwasm.github.io/docs/wasm-bindgen/introduction.html).
|
||||||
- Dioxus provides hydration to resume apps that are rendered on the server. See the [hydration example](https://github.com/DioxusLabs/dioxus/blob/master/packages/web/examples/hydrate.rs) for more details.
|
- Dioxus provides hydration to resume apps that are rendered on the server. See the [fullstack](fullstack.md) getting started guide for more information.
|
||||||
|
|
||||||
## Tooling
|
## Tooling
|
||||||
|
|
||||||
@@ -28,6 +29,7 @@ cargo install dioxus-cli
|
|||||||
```
|
```
|
||||||
|
|
||||||
Make sure the `wasm32-unknown-unknown` target for rust is installed:
|
Make sure the `wasm32-unknown-unknown` target for rust is installed:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
rustup target add wasm32-unknown-unknown
|
rustup target add wasm32-unknown-unknown
|
||||||
```
|
```
|
||||||
@@ -49,11 +51,11 @@ cargo add dioxus-web
|
|||||||
```
|
```
|
||||||
|
|
||||||
Edit your `main.rs`:
|
Edit your `main.rs`:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
{{#include ../../../examples/hello_world_web.rs}}
|
{{#include ../../../examples/hello_world_web.rs}}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
And to serve our app:
|
And to serve our app:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
Reference in new issue
Block a user