JMDC TECH BLOG

JMDCのエンジニアブログです

LaravelのUIフレームワーク、Livewireを触った話

こんにちは。JMDCインシュアランス本部ソリューション部の宮田です。

今年、JMDCではアドベントカレンダーに参加しています。 qiita.com 本記事はJMDCアドベントカレンダー 3日目の記事になります。

はじめに

Webアプリの新規開発で、LaravelのUIフレームワーク、Livewireを使う機会がありました。 Livewireはサーバー駆動型UIフレームワークです。 今まで私の経験ではクライアント駆動型UIフレームワークしか使ったことがありませんでした。 Livewireを使う上で、いいなと思った点やハマった点の共有と、自分の思考の癖でハマった際に気づきがあったので共有できればいいなと思いました。

Livewireについて

はじめにでも触れましたが、LivewireはLaravelのUIフレームワークです。 livewire.laravel.com 複雑なJavaScriptコードを書くことなく、PHPの知識だけでVueやReactのような動的でインタラクティブなUIを構築できます。 サーバーサイドとクライアントサイドのロジックをPHPに集約できるため、コードが簡素化され一元管理ができます。

メリットと感じた点

まずはメリットと感じた点です。

  • バリデーションロジック等をサーバーとクライアントで統一できること

クライアント側とサーバー側でそれぞれバリデーションロジックを作ると、エラーメッセージやロジックが微妙に食い違うことがありますが Livewireを使うと、エラーメッセージやロジックをサーバー側で統一できるので設計、実装、テストの工数を大幅に削減できます。 エラーメッセージの表示もデータバインドで自動的に行われるため、JavaScriptの使用は全く必要ありません。

  • ボイラープレートが省略できる

上記とも関連しますが、サーバー側にロジックを一元管理できることに加え、クライアント側のAPIの作成、サーバー側のルーターやコントローラーの作成が不要になるので 従来の開発で必要だった、下記のような定型的なコード作成が不要になり、開発スピードがとても上がります。

  • routes/api.php へのAPIエンドポイント定義
  • ApiController の作成
  • fetch等を使ったJavaScriptでのAPI呼び出し
  • レスポンス(JSON)を解釈してUIに反映する処理

メリットとのトレードオフと注意点

次に、Livewireを採用する上での、上記メリットとのトレードオフと注意点についてです。

  • Livewireのプロパティは、ユーザーの操作(ボタンクリック、フォーム入力など)以外での変更を検知しない。

特にクライアント側でのデータ操作に慣れている方は注意が必要です。 よくあるドラッグ&ドロップでファイルをサーバーにアップロードできる機能の作成に取り掛かったときに、 Livewireのドキュメントにアップロードの実装例があり、たったの27行で実装されていたので、使ってみようと思いました。 livewire.laravel.com

通常のエクスプローラーやFinderからファイルを選択してアップロードするのは問題なく動作するのですが、 ドラッグ&ドロップで登録されたファイルのアップロードが上手く動作しませんでした。 問題はタイトルに記載していますが、Livewireのプロパティは、ユーザーの操作(ボタンクリック、フォーム入力など)以外での変更を検知しないためです。 ドラッグ&ドロップでファイルを登録されたときに、JavaScriptでフォームに登録していたためLivewireのプロパティに反映されていないことが原因でした。 Livewireプロパティに手動で変更を通知する方法があったので、試してみたのですがfile形式では上手くいかず結局クライアントからfetchで送る方法で実装しました。

どうしてもサーバー側ではなく、クライアント側でデータを操作しないと難しい場面は多いと思うのでこの点は注意が必要だと思いました。

  • Bladeコンポーネントとの使い分けには注意が必要

BladeコンポーネントとLivewireコンポーネントの使い分けについても注意が必要です。 Livewireコンポーネントは、どのコンポーネントでどのプロパティを管理するかの設計が重要です。 各コンポーネントで、プロパティの持ち方を間違えると、想定していたようなHTMLの入れ替えが起きないことがあります。

それに加えて、特にサーバー側で管理が必要なプロパティがない場合は、積極的にBladeコンポーネントを選択する必要があります。 これはLivewireコンポーネントの大量生成は、ロードに時間がかかるためです。

このようにBladeコンポーネントとLivewireコンポーネントとの使い分けにおいて、それぞれできることと設計思想が異なっていることも頭を悩ませます。 階層があるコンポーネント間でエラーの管理をするのに苦戦しました。 Livewireでは、親子間のコンポーネントでの情報のやり取りはイベントで行います。 Bladeコンポーネントでは、そもそも親子コンポーネント間の情報のやり取りが想定されていません。 コントローラーで発生したエラーをそれに紐づくBladeコンポーネントで表示するといった閉じた使い方が想定されているためです。 さらにLivewireにもLaravelにもグローバルの状態管理をする機能がデフォルトでは準備されていませんので、必要であれば自分で実装するか JS側の力を借りるといった工夫が必要になり、PHPの知識だけで構築が行えるといったメリットを減少させてしまいます。

このようにBladeコンポーネントとLivewireコンポーネントの特性に気をつけながら、 グローバルな状態管理も使えず、JS側の力もなるべく使わないようにという条件でコンポーネントの階層間や離れたコンポーネント間のエラーの管理は難しかったです。

思考の癖について

今までの開発経験ではクライアント駆動型のUIフレームワークしか使ったことがなく、 Livewireのようなサーバー駆動型のUIフレームワークは初めて使用しました。 サーバー駆動型のUIフレームワークを使ってみて、ついクライアント側で処理を行おうという思考に偏ってしまっていることに気づきました。

データを編集するダイアログを実装する例をもとに、どういった不具合が起きたのか記載していきたいと思います。

まずデータを編集するダイアログを実装するときにクライアント駆動で考えると、ダイアログの表示/非表示はサーバーで管理する必要はないので、 クライアント側でJavaScriptを使って、表示/非表示を切り替えます。 このとき下記のように実装していくと思います。

  1. ダイアログのUIだけを作成しておく
  2. ダイアログを開くユーザーの操作で、ダイアログに表示するデータをAPIで取得する
  3. ダイアログにデータを反映して表示する

このように実装したとき、「ダイアログ内のデータを編集したが保存せずにダイアログを閉じて再度開いた」場合、 データは編集前の状態に戻っていることが期待されると思います。 コンポーネントの「破棄(アンマウント)」と「ローカルな状態(編集中のデータ)」が連動しているためです。

これをクライアント駆動とサーバー駆動が混在した状態で作成したときは、下記のような実装になります。

  1. ダイアログの表示/非表示はサーバーで管理する必要はないので、クライアント側でJavaScriptを使って切り替えるようにする。
  2. ダイアログのUIにデータを埋め込んだ状態でクライアントに送る
  3. ダイアログを開くユーザーの操作でダイアログの表示/非表示のみ切り替える

データはすでに埋め込み済みでプロパティはサーバーで管理しているので、 「ダイアログ内のデータを編集したが保存せずにダイアログを閉じて再度開いた」場合でも、 サーバー側のプロパティは変更されたままになってしまいます。

このようなことにならないようにするには、クライアント駆動とサーバー駆動を混在させず、クライアント側で管理するべき状態も必ずサーバー側で管理するようにします。 ただし、ダイアログの表示状態が非表示になったときにフレームワークが自動的にダイアログ内の状態を破棄し、再度開いたときに編集前の状態に戻すためには、さらに特定の設計パターンを採用する必要があります。 具体的には、ダイアログのUIとロジック(編集データ)を、親コンポーネントから子コンポーネントとして分離し、親側で @if ディレクティブなどを使って表示状態に応じて動的に読み込む(マウント/破棄する)設計です。 このパターンを採用することで初めて、ダイアログが非表示になったタイミングで子コンポーネントのインスタンス自体が破棄され、編集途中の状態も自動的にクリアされます。

自分が慣れ親しんでいるクライアント駆動での思考の癖で設計せずに、それぞれのフレームワークが想定している使い方で設計してあげる必要があった、よい例でした。

最近は生成AIを用いてどのようなコードにするかを考える機会が多いので、生成AIに指示を与える段階で思考の癖があると、間違った方向に導くような指示を与えてしまうこともあると思いました。 この部分については、最近発表があったVS CodeのGitHub CopilotのPlan Modeを利用するのが使えそうだったので、リリースされたら実際に活用してみたいと思います。

まとめ

使ったことのない技術を使って、自分の思考の癖に気づけたいい機会でした。 これからも積極的に使ったことのない技術を触って、既存の技術との違いについて身につけていきたいと思いました!

明日4日目は、竹内さんによる「AWSコストをもっと細かく見たいんです。~データエクスポート(CUR 2.0)活用の巻~」です。お楽しみに!

JMDCでは、ヘルスケア領域の課題解決に一緒に取り組んでいただける方を積極採用中です!フロントエンド /バックエンド/ データベースエンジニア等、様々なポジションで募集をしています。詳細は下記の募集一覧からご確認ください。 hrmos.co まずはカジュアルにJMDCメンバーと話してみたい/経験が活かせそうなポジションの話を聞いてみたい等ございましたら、下記よりエントリーいただけますと幸いです。 hrmos.co ★最新記事のお知らせはぜひ X(Twitter)、またはBlueskyをご覧ください! twitter.com bsky.app