Explorar o código

add fullstack section to the getting started guide

Evan Almloff %!s(int64=2) %!d(string=hai) anos
pai
achega
4c22d5809e

+ 2 - 1
docs/guide/Cargo.toml

@@ -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"] }

+ 132 - 0
docs/guide/examples/server.rs

@@ -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
+}

+ 1 - 0
docs/guide/src/en/SUMMARY.md

@@ -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)

+ 115 - 0
docs/guide/src/en/getting_started/fullstack.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).

+ 13 - 2
docs/guide/src/en/getting_started/hot_reload.md

@@ -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.

+ 6 - 15
docs/guide/src/en/getting_started/ssr.md

@@ -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.

+ 4 - 2
docs/guide/src/en/getting_started/web.md

@@ -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