Jaspr Web アーキテクチャガイド(v0.22.x) は何をしますか?
目標
Jaspr は Dart 製の Web フレームワークです(現在のバージョン: 0.22.x)。
Flutter Web(Canvas 経由でピクセル描画を行い、SEO に弱い)とは異なり、Jaspr は Dart をセマンティックな HTML および CSS の DOM 要素にコンパイルします。Web サイト、ブログ、ランディングページ、または適切な SEO が必要なプロジェクトには Jaspr を使用してください。
手順
1. コアコンセプト: Flutter ライクなコーディング体験
Jaspr は Flutter のコンポーネントモデルを反映しています。build メソッドは単一の Component(Flutter の Widget に相当)を返します。
| Flutter の概念 | Jaspr の概念 | 備考 |
|---|---|---|
Widget | Component | Jaspr のすべての UI 要素は Component |
StatelessWidget | StatelessComponent | 純粋な宣言的 UI、BuildContext context を受け取る |
StatefulWidget | StatefulComponent | setState、initState、createState() でライフサイクルを管理 |
InheritedWidget | InheritedComponent | コンポーネントツリーに状態を伝播 |
1.1 DOM 要素
Jaspr は DOM にレンダリングされるため、jaspr/dom.dart から HTML タグヘルパー(div、p、img など)を使用します。
import 'package:jaspr/jaspr.dart';
class ProfileCard extends StatelessComponent {
final String name;
const ProfileCard({required this.name, super.key});
@override
Component build(BuildContext context) {
return div(
classes: 'profile-card',
styles: Styles.box(width: 300.px, backgroundColor: Colors.white),
[
h2([.text(name)]), // 🌟 Dart 3.10 以降で利用可能な .text() 省略記法(Component.text)
p([
.text('Hello World!'),
]),
button(
onClick: () => print('Button successfully clicked!'),
[.text('Contact Me')]
)
]
);
}
}
2. Tailwind CSS 統合(jaspr_tailwind)
Jaspr は jaspr_tailwind を介して公式の Tailwind CSS 統合を提供します。
2.1 インストールとセットアップ
dev_dependencies:
jaspr_tailwind: ^0.1.0
Tailwind v4 の CSS ファースト構文で web/styles.tw.css を作成します:
/* web/styles.tw.css */
@import "tailwindcss";
2.2 ルートコンポーネントでコンパイル済み CSS を読み込む
ルートコンポーネントでは、Document を使用してコンパイル済みスタイルシートをリンクし、classes パラメーターを介して Tailwind クラスを使用します:
import 'package:jaspr/jaspr.dart';
class App extends StatelessComponent {
@override
Component build(BuildContext context) {
return Document(
head: [
// 🌟 jaspr_tailwind が styles.tw.css を styles.css に自動コンパイル
link(href: 'styles.css', rel: 'stylesheet'),
],
body: div(
// Tailwind クラスを直接使用、jaspr_tailwind は開発中に自動リフレッシュ
classes: 'bg-gray-100 min-h-screen flex items-center justify-center',
[
h1(classes: 'text-4xl font-bold text-blue-600', [.text('Hello Tailwind!')]),
]
),
);
}
}
3. ルーティング(jaspr_router)
Jaspr のルーティングは go_router と同様の宣言的 API です。
3.1 インストールとセットアップ
dependencies:
jaspr_router: ^0.2.0
import 'package:jaspr/jaspr.dart';
import 'package:jaspr_router/jaspr_router.dart';
class MyApp extends StatelessComponent {
@override
Component build(BuildContext context) {
return Router(
routes: [
Route(
path: '/',
builder: (context, state) => const HomePage(),
),
Route(
path: '/about',
builder: (context, state) => const AboutPage(),
),
],
);
}
}
4. SSR: 非同期バックエンドデータのプリロード
Flutter Web に対する Jaspr の主な利点はサーバーサイドレンダリング (SSR) です。Flutter Web ではデータは initState でフェッチされ、クライアント側でローディング状態が発生します。Jaspr SSR では、データはサーバー上で HTML がブラウザに送信される前にフェッチされます。
4.1 AsyncStatelessComponent(サーバー専用)
jaspr/server.dart をインポートして AsyncStatelessComponent を使用すると、非同期の build メソッドが可能になります。
import 'package:jaspr/server.dart'; // 🌟 サーバー専用インポート
class ArticlePage extends AsyncStatelessComponent {
final String articleId;
const ArticlePage(this.articleId);
@override
Future<Component> build(BuildContext context) async {
// サーバーサイドで実行。データベース/API 呼び出しに await を自由に使用可能
final articleData = await Database.fetchArticle(articleId);
// ブラウザは完全にレンダリングされた HTML を受け取るため、瞬時に SEO フレンドリーなコンテンツが表示される
return div([
h1([.text(articleData.title)]),
p([.text(articleData.content)]),
]);
}
}
4.2 ステートフルなプリロード(PreloadStateMixin)
状態を保持する必要がある場合は、PreloadStateMixin をミックスインします:
class MyState extends State<MyStatefulComponent> with PreloadStateMixin {
@override
Future<void> preloadState() async {
// サーバーサイドで initState() の前に実行。クライアント側のハイドレーションのために状態を準備
await loadInitialData();
}
// ...
}
5. テスト(jaspr_test)
jaspr_test は、コンポーネントの単体テスト、SSR 結果のテスト、ブラウザ操作のテストをカバーします。
5.1 依存関係のセットアップ
dart pub add jaspr_test --dev
jaspr_test はネイティブの package:test を再エクスポートするため、個別にインポートする必要はありません。
5.2 3つの主要なテスト関数
HTML/Web 環境の複雑さのため、Jaspr はシナリオごとに 3 つのテスト関数を提供します:
testComponents(仮想環境でのコンポーネントテスト)
ヘッドレスな純粋 Dart シミュレート環境でコンポーネントのロジックを単体テストします。最も高速な方法です。使用方法は Flutter の testWidgets と非常に似ています。
import 'package:jaspr_test/jaspr_test.dart';
void main() {
testComponents('ボタンクリックでカウントが増えることをテスト', (tester) async {
tester.pumpComponent(MyComponent());
expect(find.text('Count: 0'), findsOneComponent);
await tester.click(find.tag('button'));
expect(find.text('Count: 1'), findsOneComponent);
});
}
testServer(サーバーサイドレンダリングテスト)
SSR アプリケーションのテストを目的としています。シミュレートされたローカル HTTP サーバーを直接起動します。リクエストを送信し、サーバーが返す HTML 文字列または状態プロパティを検証できます。
import 'package:jaspr_test/jaspr_test.dart';
void main() {
testServer('サーバーサイドでレンダリングされた HTML を検証', (tester) async {
final response = await tester.request('/');
expect(response.document.body?.text, contains('Welcome to SSR'));
});
}
testClient(ブラウザのハイドレーションと状態同期テスト)
ヘッドレスブラウザ環境で実行し、サーバーから initialStateData を受け取った後、クライアントがハイドレーションを介して正しく引き継ぎ、DOM 操作を処理するかをテストします。
制約
AsyncStatelessComponentはサーバーサイド(jaspr/server.dart)でのみ使用すること。クライアント UI コードでは使用しないでください。- Flutter の
Widgetを Jaspr のComponentの中にネストしないでください。 - レンダリングモードに合わせたテスト関数を選択してください: SSR →
testServer、CSR →testComponents、ブラウザ →testClient。厳密な型チェックよりも DOM 文字列のマッチングを優先してください。