Skip to content

Instantly share code, notes, and snippets.

@Comamoca
Last active January 24, 2026 05:22
Show Gist options
  • Select an option

  • Save Comamoca/a888b6b9646084185c39701a8211c121 to your computer and use it in GitHub Desktop.

Select an option

Save Comamoca/a888b6b9646084185c39701a8211c121 to your computer and use it in GitHub Desktop.
Lustre レンダリングプロセス詳細 - Lustre Rendering Process Deep Dive

Lustre レンダリングプロセス詳細

Note

これはclaude codeが作成したドキュメントです。 間違いが含まれている可能性があることに留意してください

概要

LustreはGleam言語で実装された、Erlang/JavaScriptの両ターゲットで動作する、型安全なWebフレームワークです。このドキュメントでは、Lustreのレンダリングアーキテクチャとプロセスについて詳しく解説します。


1. 主要なレンダリング機能とモジュール

コアモジュール

  • /src/lustre.gleam - アプリケーションコンストラクタを提供するメインエントリーポイント(element, simple, application, component
  • /src/lustre/element.gleam - HTML要素を作成するための公開API
  • /src/lustre/vdom/vnode.gleam - Virtual DOMノードの定義
  • /src/lustre/vdom/diff.gleam - Virtual DOM差分検出アルゴリズム
  • /src/lustre/vdom/patch.gleam - パッチデータ構造
  • /src/lustre/vdom/reconciler.ffi.mjs - DOM調整処理(JavaScript実装)

主要な関数

  • view(model) -> Element(msg) - ユーザー定義のビュー関数。Virtual DOMを生成
  • diff(cache, old, new) -> Diff(msg) - 2つのVirtual DOMツリーを比較
  • Reconciler.push(patch, memos) - 実際のDOMにパッチを適用

2. Virtual DOM実装

Lustreは複数のノードタイプを持つ洗練されたVirtual DOM(VDOM)実装を提供します。

VDOMノードタイプ

/src/lustre/vdom/vnode.gleamより:

pub type Element(msg) {
  Fragment(kind, key, children, keyed_children)
  Element(kind, key, namespace, tag, attributes, children, keyed_children, self_closing, void)
  Text(kind, key, content)
  UnsafeInnerHtml(kind, key, namespace, tag, attributes, inner_html)
  Map(kind, key, mapper, child)      // メッセージマッピング用
  Memo(kind, key, dependencies, view) // メモ化用
}

主要機能

  • Keyedチルドレン: キー付き要素による効率的なリスト更新(MutableMapに保存)
  • メモ化: Memoノードは依存関係の参照等価性を使用して高コストな計算をキャッシュ
  • メッセージマッピング: Mapノードはコンポーネント合成のための独立したイベントサブツリーを作成
  • 文字列レンダリング: SSR用にto_string()to_string_tree()でHTML文字列へのレンダリングが可能

3. コンポーネントレンダリング

Lustreは複数のコンポーネントタイプをサポートします。

コンポーネントタイプ

  1. シンプルなビュー関数 - Element(msg)を返す通常のGleam関数
  2. ステートフルコンポーネント - init, update, viewを持つ完全なMVUアプリケーション
  3. Webコンポーネント - Shadow DOMを使用するカスタムエレメント
  4. サーバーコンポーネント - サーバー上で実行し、パッチをクライアントにストリーミング

コンポーネント設定

/src/lustre/component.gleamより、コンポーネントは以下で設定可能:

  • 属性オブザーバー(on_attribute_change
  • プロパティオブザーバー(on_property_change
  • コンテキストプロバイダー/コンシューマー(on_context_change
  • Shadow DOM設定(open_shadow_root, adopt_styles
  • フォーム関連付け(form_associated
  • コンテンツプロジェクション用スロット(default_slot, named_slot

4. サーバーサイドレンダリング vs クライアントサイドレンダリング

クライアントサイドレンダリング

/src/lustre/runtime/client/より:

SPAモード(spa.ffi.mjs

  1. Runtimeインスタンスを作成
  2. マウント時に既存DOMを仮想化
  3. requestAnimationFrameで更新ループを実行
  4. パッチを同期または一括で適用

ランタイムライフサイクル

constructor → mount → #render → diff → reconcile → paint

エフェクトスケジューリング

  • synchronous - 更新中に即座に実行
  • before_paint - ブラウザペイント前のマイクロタスクで実行
  • after_paint - ブラウザペイント後に実行

サーバーサイドレンダリング

静的SSR

  • element.to_string()またはelement.to_document_string()を呼び出し
  • HTML文字列を直接生成
  • ランタイム不要

サーバーコンポーネント(/src/lustre/runtime/server/runtime.gleam

  • Erlang/Node/Deno上で実行
  • サーバーサイドでVDOM状態を維持
  • 各更新で差分検出
  • WebSocket経由でJSONパッチを接続クライアントに送信
  • クライアントは最小限のランタイムでパッチを適用

トランスポートプロトコル

/src/lustre/runtime/transport.gleamより:

  • Mount - 初期VDOMと設定
  • Reconcile - 適用するパッチ
  • EventFired - クライアントからのユーザーインタラクション
  • AttributeChanged / PropertyChanged - クライアントから
  • ContextProvided - コンテキストインジェクション

5. 更新/パッチメカニズム

差分検出アルゴリズム

/src/lustre/vdom/diff.gleamより、diff()関数は洗練された調整アルゴリズムを実装:

コアアルゴリズム

  1. 子要素リストを順番に比較
  2. キー付き子要素を特別な移動処理で扱う
  3. 最小限の変更セットを生成

変更タイプ

/src/lustre/vdom/patch.gleamより:

pub type Change(msg) {
  ReplaceText(content)
  ReplaceInnerHtml(inner_html)
  Update(added, removed)  // 属性
  Move(key, before)       // キー付き子要素
  Replace(index, with)    // ノード置換
  Remove(index)           // ノード削除
  Insert(children, before) // ノード挿入
}

キー付き子要素の最適化

アルゴリズムが追跡するもの:

  • moved - 再配置されたキー
  • moved_offset - 移動を考慮したオフセット
  • removed - 削除された子要素の数

これにより、最小限のDOM操作で効率的なリスト調整が可能。

パッチ構造

Patch(
  index: Int,          // 子要素のインデックス
  removed: Int,        // 削除する子要素数
  changes: List(Change), // このノードへの操作
  children: List(Patch)  // 再帰的なパッチ
)

DOM調整(Reconciliation)

/src/lustre/vdom/reconciler.ffi.mjsより、reconcilerは:

  1. DOMと並行してメタデータツリーを維持
  2. イベントハンドラー、スロットル、デバウンサーを保存
  3. パッチツリーを深さ優先で走査
  4. 正確性のため変更を逆順で適用
  5. 利用可能な場合moveBefore APIを使用(新しいDOM機能)
  6. デバウンス/スロットルタイマーを管理

6. レンダリングライフサイクルの主要ステップ

完全なレンダリングフロー

[ユーザーアクション]
    ↓
[エフェクトディスパッチ] または [イベント発火]
    ↓
[update(model, msg)] → #(new_model, Effect(msg))
    ↓
[view(new_model)] → Element(msg)
    ↓
[diff(cache, old_vdom, new_vdom)] → Diff
    ↓
[パッチをJSONにエンコード](サーバーコンポーネントのみ)
    ↓
[Reconciler.push(patch, memos)]
    ↓
[パッチツリーを走査、変更を適用]
    ↓
[DOM更新]
    ↓
[before_paintエフェクト] → queueMicrotask
    ↓
[ブラウザペイント]
    ↓
[after_paintエフェクト] → requestAnimationFrame

イベント処理

  1. イベントはShadow DOMを通じてバブルアップ
  2. Reconcilerがメタデータノードでキャプチャ
  3. パス経由でハンドラーを検索(例:"0\t1\tbutton\nclick"
  4. 設定されている場合、デバウンス/スロットルを適用
  5. イベントデータをデコード
  6. update関数にメッセージをディスパッチ

キャッシュ管理

/src/lustre/vdom/cache.gleamより、キャッシュは以下を維持:

  • イベントハンドラーツリー(Map分離あり)
  • メモ化されたVDOMノード
  • ディスパッチされたイベントパス(制御された入力用)
  • 依存関係比較用の古いメモ

制御された入力

<input>, <select>, <textarea>の特別な処理:

  • どのパスがイベントをディスパッチしたかを追跡
  • ユーザーが操作した場合、属性更新を強制
  • カーソルジャンプと値の上書きを防止

アーキテクチャのハイライト

  1. ユニバーサルコンポーネント: 同じコードがクライアント/サーバー/スタンドアロンで動作
  2. 効率的なキー付き差分検出: 動的リストのDOM操作を最小化
  3. メモ化: 参照等価性チェックで高コストな再レンダリングをスキップ
  4. エフェクトバッチング: ブラウザペイントサイクル周辺のスマートスケジューリング
  5. イベント分離: Mapノードが安定したイベントサブツリーを作成
  6. サーバーコンポーネント: 最小限のクライアントランタイムでWebSocket経由の更新ストリーミング
  7. 型安全性: GleamのタイプシステムによるErlangとJavaScriptターゲット全体での強力な型付け

このアーキテクチャは、開発者の使いやすさとErlang・JavaScriptターゲット全体での型安全性を維持しながら、優れたパフォーマンスを提供します。


参照

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment