add fullstack section to the getting started guide
This commit is contained in:
1 parent
53c8585107
commit
4c22d5809e
7 files changed
+273
-20
No files matched your search
@@ -16,6 +16,7 @@ dioxus-native-core-macro = { path = "../../packages/native-core-macro" }
|
||||
dioxus-router = { path = "../../packages/router" }
|
||||
dioxus-liveview = { path = "../../packages/liveview", features = ["axum"] }
|
||||
dioxus-tui = { path = "../../packages/dioxus-tui" }
|
||||
dioxus-server = { path = "../../packages/server" }
|
||||
fermi = { path = "../../packages/fermi" }
|
||||
shipyard = "0.6.2"
|
||||
|
||||
@@ -23,5 +24,5 @@ shipyard = "0.6.2"
|
||||
# dioxus = { path = "../../packages/dioxus", features = ["desktop", "web", "ssr", "router", "fermi", "tui"] }
|
||||
serde = { version = "1.0.138", features=["derive"] }
|
||||
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"] }
|
||||
@@ -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)
|
||||
- [Web](getting_started/web.md)
|
||||
- [Server-Side Rendering](getting_started/ssr.md)
|
||||
- [Fullstack](getting_started/fullstack.md)
|
||||
- [Liveview](getting_started/liveview.md)
|
||||
- [Terminal UI](getting_started/tui.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.
|
||||
|
||||
# Web
|
||||
|
||||
For the web renderer, you can use the dioxus cli to serve your application with hot reloading enabled.
|
||||
|
||||
## Setup
|
||||
|
||||
Install [dioxus-cli](https://github.com/DioxusLabs/cli).
|
||||
Hot reloading is automatically enabled when using the web renderer on debug builds.
|
||||
|
||||
## Usage
|
||||
|
||||
1. Run:
|
||||
```bash
|
||||
|
||||
```bash
|
||||
dioxus serve --hot-reload
|
||||
```
|
||||
|
||||
2. Change some code within a rsx or render macro
|
||||
3. Open your localhost in a browser
|
||||
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.
|
||||
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.
|
||||
|
||||
## Setup
|
||||
|
||||
Add the following to your main function:
|
||||
|
||||
```rust
|
||||
@@ -37,13 +44,17 @@ fn main() {
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
1. Run:
|
||||
|
||||
```bash
|
||||
cargo run
|
||||
```
|
||||
|
||||
2. Change some code within a rsx or render macro
|
||||
3. Save and watch the style change without recompiling
|
||||
|
||||
# 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.
|
||||
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
|
||||
|
||||
The Dioxus VirtualDom can be rendered server-side.
|
||||
|
||||
[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.
|
||||
|
||||
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.
|
||||
|
||||
## Setup
|
||||
|
||||
@@ -21,7 +10,7 @@ Make sure you have Rust and Cargo installed, and then create a new project:
|
||||
|
||||
```shell
|
||||
cargo new --bin demo
|
||||
cd app
|
||||
cd demo
|
||||
```
|
||||
|
||||
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/).
|
||||
|
||||
Examples:
|
||||
|
||||
- [TodoMVC](https://github.com/DioxusLabs/example-projects/tree/master/todomvc)
|
||||
- [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.
|
||||
|
||||
- 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
|
||||
|
||||
@@ -28,6 +29,7 @@ cargo install dioxus-cli
|
||||
```
|
||||
|
||||
Make sure the `wasm32-unknown-unknown` target for rust is installed:
|
||||
|
||||
```shell
|
||||
rustup target add wasm32-unknown-unknown
|
||||
```
|
||||
@@ -49,11 +51,11 @@ cargo add dioxus-web
|
||||
```
|
||||
|
||||
Edit your `main.rs`:
|
||||
|
||||
```rust
|
||||
{{#include ../../../examples/hello_world_web.rs}}
|
||||
```
|
||||
|
||||
|
||||
And to serve our app:
|
||||
|
||||
```bash
|
||||
|
||||
Reference in new issue
Block a user