A Rust webes ökoszisztémája az elmúlt két évben rohamosan fejlődött, és a Leptos az egyik olyan projekt, amit egyszerűen nem lehet kihagyni, ha valaki full-stack Rust webfejlesztésről beszél. Ma jelent meg a 0.5-ös verzió, és őszintén szólva ez az egyik legjelentősebb frissítés, amit a keretrendszer eddig kapott.

Mi az a Leptos, és miért fontos?

A Leptos egy reaktív, WebAssembly-alapú frontend framework, amely ugyanazt a komponens-modellt használja szerver- és kliensoldalon is – innen a "full-stack" jelző. A cél, hogy egyetlen Rust kódbázisból generálódjon a szerveroldali HTML (SSR), a hidratáció, és a kliensoldali interaktivitás is, anélkül, hogy JavaScript-re vagy külön API-rétegre lenne szükség.

Azért fontos ez, mert a Rust közösségben eddig két nagy irányzat versengett: a virtuális DOM-ot használó megoldások (mint a Yew), és a signal-alapú, finomszemcsés reaktivitást választó projektek. A Leptos az utóbbi táborba tartozik, és a SolidJS-hez hasonló architektúrát ültet át Rust nyelvre.

Megjegyzés

Ha még nem találkoztál a Leptos-szal: gondolj rá úgy, mint egy Rust-os SolidJS-re, ahol a view! makró generálja a DOM-manipulációs kódot fordási időben, virtuális DOM nélkül.

A 0.5-ös verzió legfontosabb újításai

A Leptos csapata a 0.5-ös kiadásban több hónapos munka eredményét tette közzé. A legfontosabb változások:

  • Teljesen újraírt reaktivitási rendszer, amely most már finomabb granularitású frissítéseket tesz lehetővé
  • Server function makrók stabilizálása, amivel a kliens és szerver közötti hívások írása sokkal egyszerűbb
  • Jobb hidratációs teljesítmény, kevesebb felesleges DOM-összehasonlítással
  • Axum és Actix integráció frissítése, mostantól kényelmesebb a routing beállítása

Egy egyszerű komponens így néz ki a 0.5-ös API-val:

use leptos::*;

#[component]
fn Counter(initial_value: i32) -> impl IntoView {
    let (count, set_count) = create_signal(initial_value);

    view! {
        <button on:click=move |_| set_count.update(|n| *n += 1)>
            "Kattints ide: " {count}
        </button>
    }
}

Ami elsőre furcsa lehet a Yew-hoz vagy React-hoz szokott fejlesztőknek: itt nincs re-render a komponens szintjén. A count signal olvasása a view! makróban egy célzott DOM-frissítést regisztrál, ami csak azt az egy szöveges node-ot érinti, ami valóban megjelenik.

Reaktivitási modell és teljesítmény

A Leptos reaktivitási modellje a create_signal, create_memo és create_effect hármasára épül. Ezek a signal-alapú primitívek egy dependency gráfot tartanak fenn futásidőben, és amikor egy signal értéke megváltozik, csak azok a részek frissülnek, amelyek tényleg függenek tőle.

use leptos::*;

fn main() {
    let (name, set_name) = create_signal("Rust".to_string());

    // A memo csak akkor számol újra, ha a name valóban változik
    let greeting = create_memo(move |_| format!("Szia, {}!", name.get()));

    create_effect(move |_| {
        leptos::logging::log!("{}", greeting.get());
    });

    set_name.set("Leptos".to_string());
}

A 0.5-ös verzióban a csapat kifejezetten a memo-k és effect-ek ütemezését optimalizálta: a korábbi verziókban előfordultak felesleges újraszámolások, amikor egy signal többszörösen frissült egyetlen "tick" alatt. Most ezek batch-elve futnak, ami mérhetően csökkenti a CPU-terhelést nagyobb komponensfákban.

Tipp

Ha a projektedben sok egymásba ágyazott create_memo hívást használsz, érdemes megmérni a régi és az új verzió közti különbséget – a csapat blogbejegyzése szerint egyes benchmarkokban 30-40%-os gyorsulást is mértek.

A teljesítmény szempontjából érdemes megemlíteni, hogy a Leptos build-je erősen támaszkodik a fordítási idejű makró-expanzióra. A view! makró nem futásidőben parsz-olja a JSX-szerű szintaxist, hanem közvetlenül Rust kódra fordítja, ami a WebAssembly bundle méretét is kedvezően érinti.

Server-side rendering és hidratáció finomítások

Az SSR és hidratáció mindig a legkényesebb rész egy izomorf keretrendszerben, és a Leptos 0.5 itt is jelentős előrelépést hozott. A hidratáció során a szerver által legenerált HTML-t a kliens oldali WASM kód "felveszi", és rákapcsolja a reaktív event listenereket – anélkül, hogy a teljes DOM-ot újra kellene építeni.

A korábbi verziókban ez a folyamat time-to-interactive szempontból nem volt optimális nagyobb oldalaknál, mert a hidratáció szinkron módon, egyben futott le. A 0.5-ben bevezetett out-of-order streaming támogatás lehetővé teszi, hogy a szerver darabokban (chunk) küldje a HTML-t, és a kliens fokozatosan hidratáljon.

Egy egyszerű Axum-alapú szerver integráció így nézhet ki:

use axum::{routing::get, Router};
use leptos::*;
use leptos_axum::{generate_route_list, LeptosRoutes};

#[tokio::main]
async fn main() {
    let conf = get_configuration(None).await.unwrap();
    let leptos_options = conf.leptos_options;
    let addr = leptos_options.site_addr;
    let routes = generate_route_list(App);

    let app = Router::new()
        .leptos_routes(&leptos_options, routes, App)
        .fallback(leptos_axum::file_and_error_handler)
        .with_state(leptos_options);

    axum::Server::bind(&addr)
        .serve(app.into_make_service())
        .await
        .unwrap();
}

#[component]
fn App() -> impl IntoView {
    view! { <h2>"Üdv a Leptos SSR alkalmazásban!"</h2> }
}
Figyelem

Az Axum integráció API-ja a 0.4-ről 0.5-re migrálva néhány helyen módosult, például a generate_route_list szignatúrája. Ha frissítesz egy meglévő projektet, olvasd át a changelogot, mielőtt fejest ugrasz a build hibák debuggolásába.

Hogyan viszonyul a Leptos más Rust web frameworkökhöz?

A Rust WASM-alapú frontend keretrendszerek mezőnyében ma három komoly szereplő van: Yew, Dioxus és Leptos. A Yew a legrégebbi és legstabilabb, virtuális DOM-mal dolgozik, és React-szerű mentális modellt ad. A Dioxus hasonló elveket követ, de erős fókuszt tesz a multi-platform (desktop, mobil) használatra is. A Leptos ezzel szemben tudatosan elhagyja a virtuális DOM-ot, és a signal-alapú finomszemcsés reaktivitásra épít – ez különösen nagy, adatintenzív felületeken hoz mérhető teljesítménybeli előnyt.

Ha a backend oldalt nézzük, a Leptos nem versenyzik közvetlenül olyan tiszta API keretrendszerekkel, mint az Axum vagy az Actix Web – sőt, épp ezekre épül rá az SSR réteg. Aki tehát ma egy klasszikus REST API-t akar építeni, továbbra is jól teszi, ha közvetlenül az Axum 0.6-ot választja. Aki viszont egy teljes, interaktív felhasználói felületet szeretne egyetlen Rust kódbázisból, annak a Leptos egyre komolyabb alternatívát kínál a JavaScript-alapú full-stack keretrendszerekkel (Next.js, Remix) szemben.

Jó tudni

A Leptos 0.5 még mindig aktívan fejlődő projekt, az API egyes részei (különösen a server function makrók körüli finomságok) a jövőben is változhatnak. Production環境ben érdemes fixálni a verziót a Cargo.toml-ban, és óvatosan frissíteni.

Összefoglalás

A Leptos 0.5 nem csak egy inkrementális frissítés – a reaktivitási motor újraírása, a hidratáció finomítása és az Axum integráció érettebbé válása mind abba az irányba mutat, hogy a Rust full-stack webfejlesztés lassan kikerül a "kísérleti" kategóriából. Ha eddig csak távolról figyelted a projektet, most van itt az ideje, hogy kipróbáld egy kisebb side projecten – a tanulási görbe meredek lehet, de a végeredmény egy típusbiztos, gyors és valóban egységes Rust kódbázis frontendre és backendre egyaránt.