1. はじめに
ふだん、チーム開発などの現場に活かす目的で、実務を想定し個人開発しています。
この取り組みで得たいのは「新しい技術をただ学ぶ」ことではなく、課題を解決するために技術を選定・習得する力です。
この記事では、具体例として家計簿アプリを挙げます。
最初はLaravel(Blade)+jQuery(Ajax)のMVC構成で作っていたのですが、
運用していく中で開発効率や画面速度の課題が見えてきました。
実際に感じた課題と、それを解決するために
Vue.jsを使ったSPAへ段階的に移行している過程をまとめます。
この記事は自分のメモ・学習用という趣きが強いですが、
同じような課題を抱えている方の参考になれば幸いです。
2. 現在の構成
■ 技術スタック
- バックエンド: Laravel 10.x
- フロントエンド: Blade + jQuery
- DB: MySQL 8.0
- インフラ: XServer VPS(素のLinux環境)
■ 機能概要
- 支出・収入の登録、編集、削除
- カテゴリ別の集計表示
- 月次・年次のグラフ表示(Chart.js使用)
- CSVエクスポート機能
3. 実際に感じた課題
課題1: 画面開発の効率が悪い
Bladeテンプレートでは、HTMLの中にPHPのロジックが混在します。
例えば、支出一覧画面のコード:
@foreach($expenses as $expense)
<tr>
<td>{{ $expense->date }}</td>
<td>{{ $expense->category->name }}</td>
<td>¥{{ number_format($expense->amount) }}</td>
<td>
@if($expense->is_fixed)
<span class="badge">固定費</span>
@endif
</td>
</tr>
@endforeach
この書き方だと、
・表示ロジック(number_formatやバッジの出し分け)がテンプレートに散在
・再利用可能なコンポーネント化が難しい
・同じようなUI(日付フォーマット、金額表示)を何度も書く
結果、新しい画面を追加するたびに似たようなコードをコピペする羽目に。
カテゴリ別集計画面でも同じ金額フォーマット処理を書き、
グラフ画面でも同じ処理を書き...と、保守性が低下していきました。
課題2: 画面遷移が遅い
MVCは基本的に同期通信なので、毎回サーバーへのリクエスト→HTML生成→レスポンス
という流れになります。
具体的な体感:
・支出一覧 → 支出登録フォーム: 約1.2秒
・登録後に一覧へ戻る: 約1.5秒
・月次集計 → 年次集計の切り替え: 約1.8秒
合計で約4.5秒。1日に10回操作すると45秒のロスです。
特に辛かったのが、「登録→一覧確認→また登録」の繰り返し。
買い物から帰って、レシートを見ながら5件入力する時、
毎回1.5秒待つのはストレスでした。
また、ページ遷移のたびに画面全体が白くなる(再描画される)のも
UX的にイマイチだと感じていました。
課題3: Ajaxで部分的に改善したが、根本解決にならず
一部、Ajaxで非同期化してみました。
例: 支出の削除処理
$('.delete-btn').on('click', function() {
$.ajax({
url: '/expenses/' + expenseId,
method: 'DELETE',
success: function() {
// DOM操作で該当行を削除
$('tr[data-id="' + expenseId + '"]').remove();
// 合計金額も再計算して更新
updateTotalAmount();
}
});
});
これで削除は速くなったものの、新たな問題が:
・DOM操作が散在して、どこで何を更新しているか追いづらい
・削除後の合計金額計算ロジックがJS側にも必要になり、二重管理
・エラーハンドリングが不十分で、通信失敗時にUIと実データが乖離する
部分的な改善では限界があり、「フロントエンドを体系的に設計し直す必要がある」
と感じました。
4. SPAにリプレイスする理由
上記の課題を解決するため、SPAアーキテクチャへの移行を決めました。
■ 期待する効果
1. コンポーネント指向で開発効率UP
Vue.jsのSFC(Single File Component)なら、
金額表示、日付フォーマット、カテゴリバッジなどを
再利用可能なコンポーネントとして切り出せます。
2. 画面遷移の高速化
初回ロード後は必要なデータだけをAPIで取得。
ページ全体の再描画が不要になり、体感速度が大幅に向上するはず。
3. フロントエンドの状態管理を体系化
Piniaなどの状態管理ライブラリを使えば、
「どのコンポーネントがどのデータを保持しているか」が明確になります。
4. 他の開発者が参画しやすい環境へ
現状は素のLinux環境で、環境構築がやや面倒。
Docker化することで、docker-compose upだけで開発環境が立ち上がるようにします。
5. 段階的な移行計画
いきなり全てをリプレイスするのはリスクが高いので、
段階的に移行していく計画です。
【フェーズ1】Vue.jsの基礎習得(現在ここ)
・Todoアプリで基本文法を習得
・v-for, v-if, v-model, イベントハンドリング
・computed, watchの使い分け
実際に作ったもの:
- タスク追加・削除・完了チェック
- フィルタ機能(全て/未完了/完了済み)
- LocalStorageへの保存
習得期間: 約2週間(平日1時間、休日3時間)
【フェーズ2】Vue Routerでページ遷移
・/expenses(一覧), /expenses/create(登録)などのルーティング
・ナビゲーションガードで認証チェック
・クエリパラメータでの絞り込み(/expenses?category=food)
ここまでで「SPAの骨格」が完成。
【フェーズ3】バックエンドのAPI化
・Laravel側をAPI専用に変更(Laravelはそのまま使う)
・axiosで非同期通信
・ローディング状態の管理
・エラーハンドリングの統一
現在のControllerをベースに、JSON返却に変えるだけなので
そこまで大きな変更ではないはず。
【フェーズ4】TypeScript導入
・型定義でバグを事前防止
・IDEの補完が効いて開発効率UP
正直、ここまで行けるかは未定ですが、
Vue.jsに慣れてきたら挑戦したいと思っています。
【フェーズ5】Docker化
・docker-compose.ymlで環境定義
・Dockerfile(PHP, Nginx, MySQL)
・.env.exampleで環境変数管理
チーム開発を想定し、目指す状態は、
「Docker入れてcompose up叩けば動く」
6. 現在の学習状況と詰まったポイント
■ 実際に手を動かして分かったこと
1. 公式ドキュメントが分かりやすい
Vue.js公式のチュートリアルは、段階的に学べて良かったです。
特に「Reactivity Fundamentals」の章で、refとreactiveの違いが腑に落ちました。
2. Composition APIとOptions APIの選択
最初Options APIで書いていましたが、
ロジックの再利用を考えるとComposition APIの方が良さそう。
今は全てComposition APIで書き直しています。
3. 詰まったポイント: 子コンポーネントへのデータ渡し
propsで渡すのか、emitで親に通知するのか、Piniaで共有するのか。
最初は全部propsで渡そうとして、バケツリレーが発生しました。
解決策:
→ 表示用データはprops
→ ユーザー情報など複数コンポーネントで使うものはPinia
→ ボタンクリックなどのイベントはemit
この整理ができてから、コンポーネント設計がスムーズになりました。
7. まとめと今後
個人開発だからこそ、実際に使って感じた課題をもとに、
新しい技術にチャレンジできています。
MVCで動いているものを「あえて」リプレイスするのは、
一見無駄に思えるかもしれません。
でも、
・実際の課題を解決する文脈で学べる
・before/afterで技術的な効果を実感できる
・失敗してもユーザーは自分だけ
という点で、非常に良い学習機会だと感じています。
(冒頭にも書きましたが)
この記事は自分のメモ・学習用という趣きが強いですが、
同じような課題を抱えている方の参考になれば幸いです。