色々とまとめて
以下の投稿の続き的なアレです。
今回は結局、色々と便利機能あるけどどういう基準で使ったらええんや? について、
完全に個人的な見解で書いてみようかと。
使うのは自己責任ですが、参考にしてもらえたら幸いワイワイ
どうも、話がまとまらずにダラダラ書いてしまう、え~すけさんですよ。
Composableで十分?それともプラグイン?
Vue 3ではComposableが強力なため、あらゆるロジックをComposableに入れちゃえみたいなところもありますが、
実務では 「それプラグインにすべきでは?」 というケースも合ったりするのかなと。
以下はそれぞれの使い所と、判断するうえでのザックリフローチャートにしてみました。
個人的な基準
まぁ使い所が使い所なので、当たり前体操並な基準ではありますが。。。
- Composable → 局所的・再利用ロジック
- プラグイン → 全体に関わる仕組み・ルール
もう少し踏み込んで書いてみるとこんな感じ
Composableで十分なケース
特徴
- コンポーネント単位、または一部の画面で使う
- 初期化が不要 or 軽い
- 依存関係がシンプル
- UIロジックに近い
例1:データ取得ロジック
export function useUser(id: string) {
const user = ref(null)
const loading = ref(false)
const fetchUser = async () => {
loading.value = true
user.value = await fetch(`/api/users/${id}`).then(r => r.json())
loading.value = false
}
return { user, loading, fetchUser }
}
特定の用途で完結する系のアレ
例2:フォーム状態管理
export function useForm() {
const values = reactive({})
const errors = reactive({})
const validate = () => { /* ... */ }
return { values, errors, validate }
}
UIに近いロジック
例3:UI振る舞い
- モーダル制御
- スクロール制御
- フォーカス管理
コンポーネント寄りなアレ
プラグインにすべきケース
特徴
- アプリ全体で使う
- 初期化・設定が必要
- 依存注入したい
- ポリシーやルールを統一したい
例1:APIクライアント
export default {
install(app, options) {
const client = createApiClient(options)
app.provide('api', client)
app.config.globalProperties.$api = client
}
}
認証・エラー処理・URL管理を一元化
例2:認証・認可
app.provide('auth', {
user,
isAuthenticated,
hasPermission,
})
全画面で使う状態
例3:グローバルエラーハンドリング
app.config.errorHandler = (err) => {
// ログ送信・通知
}
アプリ全体の振る舞い
例4:設定・Feature Flag
app.provide('config', {
isEnabled: (key) => { /* ... */ }
})
環境差分・機能制御
判断フローチャート
1. それは「アプリ全体のルール」か?
- YES → プラグイン
- NO → 次へ
2. 初期化や設定が必要か?
- YES → プラグイン
- NO → 次へ
3. 複数箇所で使うロジックか?
- YES → Composable
- NO → コンポーネント内でOK
よくあるミス
なんでもComposableにする
// API設定まで含めてしまう
useApiClient()
- インスタンスがバラバラになる
- 設定が分散する
なんでもプラグインにする
app.use(useUser)
- スコープが広すぎる
- テストしづらい
- 依存が見えない
ハイブリッド構成
実際の現場ではこうなるのかなと
- プラグイン → 基盤を提供
- Composable → それを使ってロジックを組む
例
// plugin
app.provide('api', apiClient)
// composable
export function useUser() {
const api = inject('api')
const getUser = (id: string) => {
return api.get(`/users/${id}`)
}
return { getUser }
}
役割が明確に分離されるね
まとめると
- Composableは「ロジックの再利用」
- プラグインは「仕組みの共有」
- グローバルにする理由があるかを常に考える
Composableとプラグインは競合するものでなく、レイヤーが違うだけ かなと考えてます。
- 下層(インフラ) → プラグイン
- 上層(ユースケース) → Composable
この構造を意識するだけ。
超シンプルに判断するのであれば
さんざん色々と言ってきましたが、とりあえず最初は大体コレでいいかなと思ってきたわ。
「それ、差し替えたいと思うか?」
- YES → プラグイン(DIする)
- NO → Composable
元も子もないw