Leptos: фулстек на Rust без виртуального DOM
ТехнологииService Lab.
Этот сайт собран на Leptos: страницы рендерит на Rust сервер под управлением Axum, а интерактив — квиз «Рассчитаем проект за 2 минуты», фильтры кейсов — оживает в браузере на WebAssembly. Один язык, одна система типов и одна экосистема на весь стек. Разбираемся, как устроен этот фреймворк, чем он отличается от привычных JavaScript-инструментов и в каких проектах имеет смысл.
Leptos — фулстек-фреймворк: на нём пишут и серверный рендеринг, и клиентские интерфейсы. Ключевая идея — принести в Rust тонкодробную реактивность (fine-grained reactivity), подход, который прославил SolidJS и который сами авторы Leptos называют источником вдохновения, — и дополнить её SSR, гидрацией и серверными функциями «из коробки».
Сигналы вместо виртуального DOM
В React изменение состояния запускает перерисовку компонента: виртуальный DOM сравнивается с прошлой версией, и разница применяется к странице. Leptos идёт другим путём. Состояние — это сигналы, и каждый фрагмент DOM подписан только на те сигналы, от которых зависит. Изменился сигнал — обновился ровно один текстовый узел или атрибут. Ни diffing, ни повторного выполнения компонента нет в принципе.
use leptos::prelude::*;
#[component]
pub fn Counter() -> impl IntoView {
let (count, set_count) = signal(0);
view! {
<button on:click=move |_| set_count.update(|n| *n += 1)>
"Счётчик: " {count}
</button>
}
}
Выражение {count} внутри view! подписывается на сигнал напрямую: при клике обновится только текст на кнопке, а не весь компонент. Благодаря макросу разметка проверяется компилятором — опечатка в атрибуте или несовпадение типов пропсов это ошибка сборки, а не баг в рантайме браузера.
Заимствованная у Solid модель даёт знакомую эргономику: сигналы, мемоизация, эффекты. Подробнее об этом подходе в JavaScript-мире — в обзоре SolidJS.
Один стек: SSR, гидрация и серверные функции
Leptos рендерит страницу на сервере, отдаёт готовый HTML и «оживляет» его в браузере — гидрация без повторного рендера. Для общения клиента с сервером есть серверные функции:
#[server]
pub async fn submit_quiz(contact: String) -> Result<bool, ServerFnError> {
// выполняется только на сервере: БД, SMTP, секреты
save_lead(&contact).await?;
Ok(true)
}
Снаружи это обычная async-функция: клиент вызывает её как локальную, а компилятор генерирует RPC-обёртку. Тело функции выполняется только на сервере — там доступны база данных, отправка почты и всё, что не должно попадать в браузер. Под капотом Leptos интегрируется с Axum или Actix Web; наш сайт работает поверх Axum.
Сборка двух таргетов описывается декларативно:
[features]
hydrate = ["leptos/hydrate", "dep:wasm-bindgen"]
ssr = ["dep:axum", "dep:leptos_axum", "leptos/ssr"]
Инструмент cargo-leptos собирает серверный бинарник и WASM-бандл для браузера. Для клиентского профиля включают opt-level = "z" и LTO — на таких настройках WASM получается компактным. А за счёт SSR и гидрации в браузер не грузится лишний JavaScript: каркас страницы приходит готовым с сервера, и на клиент не выносятся ни шаблоны, ни данные.
Сильные и слабые стороны
- Тонкодробная реактивность: обновления DOM без diffing, быстрый отклик
- Один язык и одна система типов для клиента и сервера — рефакторинг по всему стеку
- SSR, SEO и серверные функции из коробки
- Компактный рантайм: виртуальный DOM не тянется в браузер
- Ошибки разметки и типов ловятся на этапе компиляции
Обратная сторона:
- Экосистема заметно меньше, чем у React или Vue: меньше готовых UI-китов и интеграций
- Порог входа выше: нужны и Rust с borrow checker, и реактивная модель мышления
- Найм веб-разработчиков на Rust сложнее, чем на мейнстримные стеки
- JS-библиотеки (карты, чаты, аналитика) приходится оборачивать вручную
Когда выбирать Leptos
Leptos хорош, когда проекту нужны и SEO, и живой интерфейс: лендинги с формами, SaaS-панели, админки, внутренние сервисы. Особенно выигрышен он для команд, которые уже пишут бэкенд на Rust: фулстек на одном языке убирает целый слой проблем на стыке. Если же задача — массовый продукт на готовых компонентах и рынок диктует JavaScript, зрелость экосистемы React по-прежнему перевешивает.
Заключение
Leptos — самый цельный ответ на вопрос «зачем Rust в вебе»: одна кодовая база обслуживает и сервер, и браузер, а производительность и типобезопасность перестают быть компромиссом. Фреймворк ещё молод, и его экосистема догоняет мейнстрим, — но именно за такими инструментами будущее типизированного веба.
Этот сайт — живой пример: он полностью собран на Leptos и Axum. О том, почему собственные сервисы мы пишем на Rust, — в статье Rust и Zig в привычных приложениях.
Обсудим ваш проект?
Расскажите о вашей задаче — вернёмся с оценкой сроков и стоимости в течение двух рабочих дней.