👏

Laravel Fragments - livewireを使わずに局所動的ページ更新

に公開

Laravel Fragments - 動的ページ更新のための新機能

Laravel Fragmentsは、ページの特定部分のみを更新できる新しいBlade機能です。AJAXやJavaScriptフレームワークに依存せずに、動的なユーザー体験を提供します。

概要

Fragmentsを使用すると、ページ全体をリロードすることなく、特定のセクションのみを更新できます。これにより、よりスムーズなユーザー体験と、サーバーリソースの効率的な使用が可能になります。

基本的な使用方法

Fragmentの定義

{{-- resources/views/products/index.blade.php --}}
@extends('layouts.app')

@section('content')
    <h1>商品一覧</h1>
    
    @fragment('product-list')
        <div class="products">
            @foreach($products as $product)
                <div class="product-item">
                    <h3>{{ $product->name }}</h3>
                    <p>¥{{ number_format($product->price) }}</p>
                </div>
            @endforeach
        </div>
        
        {{ $products->links() }}
    @endfragment
@endsection

コントローラーでの処理

class ProductController extends Controller
{
    public function index(Request $request)
    {
        $products = Product::paginate(10);
        
        // Fragmentリクエストの場合
        if ($request->wantsFragment('product-list')) {
            return view('products.index', compact('products'))
                ->fragment('product-list');
        }
        
        // 通常のリクエスト
        return view('products.index', compact('products'));
    }
}

JavaScriptとの統合

自動Fragment更新

// Laravel Fragmentsのヘルパーを使用
document.addEventListener('DOMContentLoaded', function() {
    // ページネーションリンクの自動Fragment化
    Fragment.hijackLinks('.pagination a', 'product-list');
    
    // フォーム送信の自動Fragment化
    Fragment.hijackForm('#filter-form', 'product-list');
});

手動でのFragment取得

// Fetchを使用した手動更新
async function updateProductList(page = 1) {
    const response = await fetch(`/products?page=${page}`, {
        headers: {
            'X-Fragment': 'product-list',
            'Accept': 'text/html'
        }
    });
    
    if (response.ok) {
        const html = await response.text();
        document.getElementById('product-list').outerHTML = html;
    }
}

高度な使用例

複数のFragmentの同時更新

{{-- カートページ --}}
@fragment('cart-items')
    @foreach($cartItems as $item)
        <div class="cart-item">
            {{ $item->product->name }}
            <input type="number" 
                   name="quantity" 
                   value="{{ $item->quantity }}"
                   data-update-fragment="cart-items,cart-summary">
        </div>
    @endforeach
@endfragment

@fragment('cart-summary')
    <div class="summary">
        <p>小計: ¥{{ number_format($subtotal) }}</p>
        <p>税込: ¥{{ number_format($total) }}</p>
    </div>
@endfragment

条件付きFragment

public function update(Request $request, Product $product)
{
    $product->update($request->validated());
    
    if ($request->wantsFragment()) {
        // 成功メッセージを含むFragmentを返す
        return view('products.show', compact('product'))
            ->fragment($request->fragment())
            ->with('success', '商品が更新されました');
    }
    
    return redirect()->route('products.show', $product)
        ->with('success', '商品が更新されました');
}

ネストされたFragment

@fragment('comments-section')
    <div class="comments">
        <h3>コメント ({{ $comments->count() }})</h3>
        
        @fragment('comments-list')
            @foreach($comments as $comment)
                <div class="comment">
                    <strong>{{ $comment->user->name }}</strong>
                    <p>{{ $comment->body }}</p>
                </div>
            @endforeach
        @endfragment
        
        @fragment('comment-form')
            <form method="POST" action="{{ route('comments.store') }}">
                @csrf
                <textarea name="body"></textarea>
                <button type="submit">投稿</button>
            </form>
        @endfragment
    </div>
@endfragment

Livewireとの比較

機能 Fragments Livewire
サーバーサイドレンダリング
JavaScriptの必要性 最小限 必須
リアルタイム更新 手動 自動
学習曲線 低い 中程度
ファイルサイズ 小さい 大きい

パフォーマンスの考慮事項

  1. キャッシング: Fragmentsは個別にキャッシュ可能
return view('products.index', compact('products'))
    ->fragment('product-list')
    ->remember(300); // 5分間キャッシュ
  1. 遅延読み込み: スクロール時の自動読み込み
Fragment.lazyLoad('.load-more', 'next-products');
  1. デバウンス: 頻繁な更新の制御
Fragment.debounce('#search-input', 'search-results', 300);

ベストプラクティス

  1. Fragment名の命名規則: ケバブケースを使用(user-profilecart-summary
  2. エラーハンドリング: Fragment更新失敗時のフォールバック
  3. アクセシビリティ: スクリーンリーダー向けのARIA属性
  4. SEO対策: 初回読み込みは完全なHTMLを返す

まとめ

Laravel Fragmentsは、モダンなWebアプリケーションに必要な動的更新機能を、シンプルかつ効率的に実装する方法を提供します。複雑なJavaScriptフレームワークを導入することなく、優れたユーザー体験を実現できます。


出典: Laravel News Blog

Laravelダイジェスト

Discussion