Обзор фреймворка Jaspr (Dart)

Я не нашел на хабре достойного представления фрейморка Jaspr сообществу и решил представить сам. Flutter, безусловно, хорошая магия: один код для всех платформ, в том числе и Web. Но за магию приходится платить. Платить мегабайтами загрузки, муками с SEO и ощущением, что ты принёс на пляж боулинг — вроде и весело, но как-то не к месту. Команда Flutter и сама честно говорит: Flutter Web создан для веб-приложений, а не для веб-сайтов. Для сложных дашбордов, PWA, для всего, что живёт за логином — да. Для контентного сайта, блога, лендинга — увы.

И вот, пока мы спорим, нужен ли Dart в вебе и не проще ли взять старый добрый TypeScript, из тени вышел проект, который предлагает не воевать с вебом, а подружиться с ним.

Знакомьтесь, Jaspr. Веб-фреймворк на Dart, который осознанно отказывается от канваса и пиксельной магии Flutter в пользу старого доброго HTML и CSS. Он выглядит как Flutter, ощущается как Flutter, но на выходе даёт то, что поисковики и браузеры любят и понимают: обычный HTML-документ и DOM. А попробовать на вкус его можно здесь.

Это не «ещё один фреймворк». Это другой подход. И, возможно, именно тот, которого так не хватало экосистеме Dart, чтобы наконец-то стать полноценным фулстек-инструментом.

Философский камень: в чём фундаментальное отличие от Flutter Web?
Чтобы понять Jaspr, нужно понять его главный принцип: он не абстрагируется от веба, а принимает его. Flutter Web говорит: «Неважно, что ��од капотом — браузер, десктоп или холодильник. Ты пишешь на Flutter, а я сам разберусь, как это нарисовать». Он создаёт свой мир внутри тега <canvas> и полностью контролирует каждый пиксель.

Jaspr говорит иначе: «Ты в браузере. У тебя есть DOM, есть CSS, есть семантические теги. Давай использовать их». Когда вы пишете в Jaspr компонент p([text('Hello Habr')]), он не рисует пиксели, имитирующие текст. Он генерирует нативный тег <p>Hello Habr</p>.

Это простое, но кардинальное различие меняет всё:

Производительность и SEO. Вместо многомегабайтного движка для рендеринга пользователь получает лёгкий HTML. Страница отображается заметно быстрее, поиск видит осмысленный контент, а не flutter.js и пустой <body>. Для любого контентного или коммерческого сайта это важный показатель.

Экосистема. Вы можете использовать любую CSS-библиотеку. Tailwind, Bootstrap, Bulma — что угодно. Вы можете интегрировать существующие JavaScript-библиотеки. Вы работаете с вебом, а не внутри «чёрного ящика».

Привычные инструменты. Забудьте о Row и Column. Для раскладки вы используете Flexbox и Grid — стандартные, мощные и предсказуемые инструменты CSS. Это заставляет переключить мышление с «мобильного» на «вебовое», но в долгосрочной перспективе это правильный путь.

Jaspr — это не попытка запустить мобильное приложение в браузере. Это полноценный веб-фреймворк, который просто использует очень знакомый и удобный синтаксис, вдохновлённый Flutter.


Архитектура «под капотом»: SSR, SSG, гидратация и другие страшные слова
Jaspr — это не просто библиотека для рендеринга. Это современный фулстек-фреймворк, который поддерживает все актуальные режимы работы. И всё это — на чистом Dart.

SSG (Static Site Generation). Генерация статического сайта. Вы пишете код, запускаете jaspr build, и на выходе получаете пачку HTML, CSS и JS-файлов, которые можно хостить где угодно — от GitHub Pages до любого CDN. Идеально для блогов, лендингов, сайтов-документаций. Кстати, официальные сайты dart.dev и docs.flutter.dev собираются с помощью Jaspr, что немного намекает на серьёзность проекта.

SSR (Server-Side Rendering). Рендеринг на стороне сервера. Пользователь запрашивает страницу, ваш сервер на Dart генерирует HTML и немедленно отдаёт его в браузер. Пользователь видит контент сразу, а поисковики счастливы.

Client-Side Rendering (SPA). Классический одностраничник. Сервер отдаёт минимальный HTML, а дальше всё рендерится на клиенте.

Автоматическая гидратация. Это самая мякотка. В режимах SSG и SSR, после того как пользователь получил готовый HTML, Jaspr незаметно подгружает клиентский JS, «оживляет» страницу, восстанавливает состояние компонентов и навешивает обработчики событий. Статический сайт превращается в интерактивное SPA без единого скачка или перезагрузки.

Все эти подходы можно комбинировать. Например, главную страницу отрендерить статически, а личный кабинет — на клиенте. Такая гибкость даёт огромные возможности для оптимизации.


Как это выглядит в коде? Придётся ли переучиваться?
Если вы писали на Flutter, то 80% синтаксиса Jaspr вам уже знакомы.

import 'package:jaspr/jaspr.dart';

void main() {
  runApp(App());
}

class App extends StatefulComponent {
  const App({super.key});

  @override
  State<App> createState() => _AppState();
}

class _AppState extends State<App> {
  int count = 0;

  @override
  Component build(BuildContext context) {
    return div([
      text('Count is $count'),
      button(
        onClick: () {
          setState(() => count++);
        },
        [text('Press Me')],
      ),
    ]);
  }
}

Что мы здесь видим?

  • StatelessComponent и StatefulComponent — старые добрые знакомые.

  • setState() работает точно так же, как мы привыкли.

  • Декларативный подход: мы описываем состояние UI, а фреймворк сам разбирается, как его обновить в DOM.

Но есть и важные отличия, продиктованные самой природой веба:

  • Возвращаемый тип Iterable<Component>. Метод build возвращает не один виджет, а итерируемую последовательность. Это позволяет вернуть несколько компонентов на одном уровне, как это естественно для HTML (<li>Item 1</li><li>Item 2</li>). Для этого используется синтаксис sync* и ключевое слово yield.

  • HTML-теги вместо виджетов. Вместо Text, Container, Padding вы используете функции text(), div(), p(), img(), которые напрямую соответствуют HTML-тегам.

  • Стилизация через CSS. Стили — это не TextStyle или BoxDecoration. Это обычные CSS-классы или инлайн-стили, которые вы передаёте в компонент. Jaspr не навязывает вам свой способ стилизации, а даёт работать с нативными инструментами.


Dart для фулстека. Почему это имеет смысл?
Главное преимущество Jaspr — это возможность построить всё приложение, от фронтенда до бэкенда и базы данных, на одном языке. Dart для этой роли подходит идеально:

  • Строгая типизация и null-safety. После хаоса в мире JavaScript это как глоток свежего воздуха. Большая часть ошибок отлавливается на этапе компиляции.

  • Общий код. Вы можете использовать одни и те же модели данных, бизнес-логику и утилиты на клиенте и на сервере. Больше никаких проблем с синхронизацией и сериализацией.

  • Производительность. На сервере Dart компилируется в быстрый нативный код, а в разработке есть JIT-компиляция с хот-релоадом.

  • Зрелая экосистема. Большинство типичных пакетов с pub.dev (HTTP-клиенты, работа с данными, управление состоянием), не завязанных на Flutter, прекрасно работают в Jaspr-проекте.

Jaspr отлично интегрируется с серверными фреймворками на Dart, такими как Shelf, Dart Frog или Serverpod. Это позволяет строить по-настоящему монолитные (в хорошем смысле) и при этом поддерживаемые системы.


Управление состоянием: Riverpod и старые друзья
Как управлять состоянием? Так же, как вы привыкли. Поскольку Jaspr — это Dart, вы можете использовать знакомые пакеты:

  • jaspr_riverpod: специальная версия Riverpod для Jaspr, которая поддерживает SSR и синхронизацию состояния между сервером и клиентом.

  • jaspr_bloc: порт flutter_bloc для любителей BLoC-подхода.

  • Provider, GetIt и любые другие DI-контейнеры и стейт-менеджеры, не завязанные на Flutter, тоже будут работать.

    Пример RiverPod:

import 'package:jaspr/jaspr.dart';
import 'package:jaspr_riverpod/jaspr_riverpod.dart';

void main() {
  runApp(ProviderScope(child: App()));
}

final counterProvider = StateProvider((ref) => 0);

class App extends StatelessComponent {
  const App({super.key});

  @override
  Component build(BuildContext context) {
    return div([
      Builder(
        builder: (context) {
          var count = context.watch(counterProvider);
          return text('Count is $count');
        },
      ),
      button(
        onClick: () {
          context.read(counterProvider.notifier).state++;
        },
        [text('Press Me')],
      ),
    ]);
  }
}

Вам не нужно учить Redux, MobX или Zustand. Вы остаётесь в привычной и комфортной экосистеме.


Ложка дёгтя: чего не хватает?
Jaspr — молодой фреймворк, и было бы нечестно говорить, что он идеален.

  • Нет готовых UI-компонентов. Здесь нет аналогов Material или Cupertino. Кнопки, поля ввода, модальные окна — всё это придётся стилизовать с нуля или использовать сторонние CSS-фреймворки. Это не минус, а особенность, но к этому нужно быть готовым.

  • Небольшое сообщество. Пока что сообщество Jaspr невелико. Найти готовое решение на Stack Overflow будет сложнее, чем для React. Зато есть активный Discord-сервер, куда зовут прямо из официальных ресурсов Jaspr — там можно получить помощь, в том числе от автора фреймворка.

  • Экосистема в стадии становления. Ещё не для всего есть готовые «jaspr-обёртки». Иногда придётся писать немного кода для интеграции с JS-библиотеками, используя dart:js_interop.


Так кому и зачем нужен Jaspr?
Jaspr — это не «убийца React» и не замена Flutter Web. Это прагматичный инструмент для конкретных задач.

Jaspr — ваш выбор, если:

  • Вы Flutter-разработчик и вам нужен сайт: лендинг, блог, документация. Вы сделаете его на знакомом языке и сэкономите массу времени.

  • Вы хотите писать фулстек на Dart: шарить код между клиентом и сервером и иметь единую кодовую базу.

  • Вам нужен быстрый, SEO-дружелюбный сайт: новостной портал, маркетплейс, любой проект, где важна скорость первой загрузки и индексация.

  • Вы устали от JavaScript-фреймворков и хотите попробовать что-то новое, но при этом строго типизированное и предсказуемое.


Попробовать самому. Это проще, чем кажется
Начать работать с Jaspr невероятно просто.

Устанавливаем CLI:

dart pub global activate jaspr_cli

Создаём проект:

jaspr create my_awesome_site

CLI предложит пройти небольшой мастер и выбрать параметры проекта (в том числе режим рендеринга: static, server или client).

Запускаем в режиме разработки:

cd my_awesome_site
jaspr serve

Сервер поднимется с хот-релоадом, и можно начинать творить.

А для тех, кто хочет просто «пощупать» фреймворк без установки, есть онлайн-песочница JasprPad — аналог DartPad, написанный на самом Jaspr.


Заключение: тихая революция
Jaspr не кричит о себе на каждом углу. Он просто делает свою работу: даёт Dart-разработчикам возможность создавать современные, быстрые и нативные для веба сайты. Он заполняет ту самую нишу, которую не смог (и не должен был) закрыть Flutter Web.

Это эволюционный шаг для всей экосистемы Dart — шаг от «языка для мобилок» к полноценному инструменту для фулстек-разработки. И, возможно, через пару лет, когда очередной JavaScript-фреймворк объявит о своей смерти, мы просто пожмём плечами. Ведь у нас будет свой, скучный, надёжный и очень мощный инструмент на строгоми четком языке программирования. И имя ему — Jaspr.