axum の上で動く Web フレームワーク
cargo build だけでビルドでき、Node も wasm も要りません。画面とサーバーの食い違いはコンパイルエラーになります。
cargo build だけでビルドでき、Node も wasm も要りません。gzip で約 14 KB のランタイムをビルド済みの状態でクレートに埋め込んでいます。
シグナルの名前、式、アクション、リンクは Rust の型から作られます。ハンドラのないページへのリンクや、パスのパラメータやクエリがページに合わないリンクは、画面とサーバーのほかの食い違いと同じくコンパイルエラーになります。
文はブラウザの中で画面を変え、一緒に送ったアクションの呼び出しはそのあとでサーバーに届きます。サーバーの応答を待たずに画面を変える処理を、追加のコードなしで書けます。
hypersig はフレームワーク本体です。同じリポジトリにある 3 つのクレートが、型付きのスタイル、アクセシブルなコンポーネント、型で検査する翻訳を足します。それぞれにドキュメントがあります。
hypersig_style型付きの atomic CSS です。プロパティはすべてメソッド、キーワードはすべてバリアントで、トークン、テーマ、条件も書けます。hypersig::style から使います。
hypersig_uishadcn/ui の見た目を持つアクセシブルなコンポーネントです。ネイティブ要素を使い、WAI-ARIA Authoring Practices に沿って作っています。
コンポーネントを見る →hypersig_i18n型で検査する翻訳です。どれかの訳が欠けた文はコンパイルエラーになり、言語は各ページのルートの一部です。
多言語化を見る →ページの状態はシグナル構造体が持ちます。Counter::count() はそのフィールドへの型付きの参照で、count.set(count + 1) はクリックのたびにブラウザがサーバーに問い合わせずに実行する文です。サーバーがカウントを入れた状態でページをレンダリングするので、ページはランタイムを読み込む前から表示されます。
実行結果
use hypersig::html::{div, span};
use hypersig::style::Style;
use hypersig::style::pattern::hstack;
use hypersig::style::value::{FontWeight, TextAlign, rem};
use hypersig::{Page, Signals};
use hypersig_ui::{ButtonSize, ButtonVariant, button};
use serde::{Deserialize, Serialize};
#[derive(Signals, Serialize, Deserialize)]
pub struct Counter {
count: i32,
}
pub fn page() -> Page<Counter> {
let count = Counter::count();
Page::new(
"カウンター",
Counter { count: 0 },
)
.child(
div()
.css(hstack(rem(1)))
.child(
button(ButtonVariant::Outline, ButtonSize::Default)
.on_click(count.set(count - 1))
.text("-1"),
)
.child(
span()
.css(
Style::new()
.min_width(rem(3))
.text_align(TextAlign::Center)
.font_size(rem(1.5))
.font_weight(FontWeight::Number(600.0)),
)
.bind_text(count.to_text()),
)
.child(
button(ButtonVariant::Default, ButtonSize::Default)
.on_click(count.set(count + 1))
.text("+1"),
),
)
}hypersig に近いのは Topcoat と Datastar です。どちらもブラウザの中にシグナルを持ち、ブラウザ向けのビルドなしにサーバーから画面を更新します。Topcoat はブラウザで動く式も Rust で書きます。
| Topcoat と Datastar | hypersig | |
|---|---|---|
| シグナル名と URL(Datastar) | HTML の属性に文字列で書きます。打ち間違いはブラウザが実行するまで分かりません | Rust の値から書きます。打ち間違いがあると cargo build が止まります |
| リンクのパラメータ(Topcoat) | href! が検査するのはハンドラだけです。パスパラメータが足りないか多いと、ページを描くときにパニックし、リクエストは 500 を返します | Route がパラメータを型の付いた値で受け取ります。間違えるとコンパイルエラーになります |
| 一覧への追加(Topcoat) | 式から項目を足せないので、サーバーが答えて描き直したときに一覧が変わります | 一覧をシグナルに持つので、サーバーが答える前にブラウザが変更を表示します |
| hypersig | Topcoat | React | Next.js | TanStack Start | Nuxt | Datastar | |
|---|---|---|---|---|---|---|---|
| 画面を書く言語 | Rust | Rust | TypeScript | TypeScript | TypeScript | TypeScript と Vue のテンプレート | HTML の属性 |
| サーバーを書く言語 | Rust | Rust | 任意 | TypeScript | TypeScript | TypeScript | 任意 |
| ブラウザ向けのビルドが不要 | はい | はい | いいえ(Vite) | いいえ(Node のバンドラー) | いいえ(Vite) | いいえ(Vite) | はい |
| 送る値の型をコンパイル時に検査 | はい | はい | いいえ | はい | はい | いいえ | いいえ |
| リンクの行き先をコンパイル時に検査 | はい | ハンドラだけ(パラメータは実行時) | いいえ(ルーターなし) | 設定で有効化(typedRoutes) | はい | 実験的(typedPages) | いいえ |
| ブラウザの中で状態を持てる | はい | はい | はい | はい | はい | はい | はい |
追加、チェック、削除をサーバーに保存する 2000 件の TodoMVC を、Apple M4 Max の Chrome 155 で 3 回測った中央値です。CPU を 4 倍に減速し、通信のたびに 50 ms の遅延を足しています。測ったアプリと計測の仕組みは bench/compare にあります。
| hypersig | Topcoat | React | Next.js | TanStack Start | Nuxt | Datastar | |
|---|---|---|---|---|---|---|---|
| JS(gzip) | 19.2 KB | 14.8 KB | 67.1 KB | 133.3 KB | 119.5 KB | 59.2 KB | 13.1 KB |
| First Contentful Paint | 186 ms | 165 ms | 196 ms | 198 ms | 193 ms | 177 ms | 182 ms |
| Total Blocking Time | 0 ms | 313 ms | 5 ms | 10 ms | 0 ms | 29 ms | 16 ms |
| Interaction to Next Paint | 24 ms | 24 ms | 24 ms | 24 ms | 24 ms | 16 ms | 16 ms |
同じページを hypersig::style、Tailwind CSS、Panda CSS、StyleX、vanilla-extract でスタイルした計測は、スタイル のページにあります。