pinia-v3 について
Vue と Nuxt の開発者向けに、Vue の状態管理ライブラリ Pinia v3 を扱うスキルガイドです。ストア設計、SSR、テスト、HMR、Vuex からの移行を説明します。
主な機能
- 012 種類の構文によるストア定義
- 02状態、ゲッター、アクションの管理
- 03プラグインとコンポーザブルの統合
- 04SSR と Nuxt の状態ハイドレーション
- 05ストアとコンポーネントのテスト
- 06HMR の設定と Vuex からの移行
活用シーン
- 01Vue アプリの共有状態の整理
- 02SSR アプリでのストアのハイドレーション
- 03状態を分離したストアのテスト
- 04Vuex モジュールから Pinia への移行
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ secondsky/ claude-skills --skill pinia-v3インストール後、Claude Code で/pinia-v3を入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
assets/example-template.txtreferences/example-reference.mdplugins-composables.mdssr-and-nuxt.mdstate-getters-actions.mdstore-syntax-guide.mdtesting-guide.mdvuex-migration-checklist.mdvuex-migration.mdscripts/api-store-example.tsauth-store-example.tsexample-script.shoption-store-template.tspersistence-plugin.tssetup-store-template.ts詳細
Pinia v3 - Vue 状態管理
ステータス:本番環境対応済み ✅ 最終更新日:2025-11-11 依存関係:Vue 3(または @vue/composition-api を使用した Vue 2.7) 最新バージョン:pinia@^3.0.4, @pinia/nuxt@^0.11.2, @pinia/testing@^1.0.2
クイックスタート(5 分)
1. Pinia をインストールする
bun add pinia
# or
bun add pinia
# or
bun add pinia
Vue <2.7 のユーザー向け:bun add @vue/composition-api で @vue/composition-api もインストールしてください
重要な理由:
- Pinia は Vue 公式の状態管理ライブラリです
- Vuex よりも優れた TypeScript サポートを提供します
- ミューテーションと名前空間の複雑さを解消します
- タイムトラベルデバッグを含む、DevTools の完全なサポートを備えています
2. Pinia インスタンスを作成して登録する
// main.ts
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
const pinia = createPinia()
const app = createApp(App)
app.use(pinia)
app.mount('#app')
重要:
- ストアを使用する前に Pinia をインストールしてください
- アプリをマウントする前に
app.use(pinia)を呼び出してください - アプリケーションごとに Pinia インスタンスは 1 つだけにしてください(SSR の場合を除く)
3. 最初のストアを定義する
// stores/counter.ts
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0,
name: 'Eduardo'
}),
getters: {
doubleCount: (state) => state.count * 2
},
actions: {
increment() {
this.count++
}
}
})
4. コンポーネントでストアを使用する
<script setup>
import { useCounterStore } from '@/stores/counter'
const counter = useCounterStore()
</script>
<template>
<div>
<p>Count: {{ counter.count }}</p>
<p>Double: {{ counter.doubleCount }}</p>
<button @click="counter.increment">Increment</button>
</div>
</template>
2 種類のストア構文
Option ストアと Setup ストアの詳細な比較については、references/store-syntax-guide.md を読み込んでください。
クイック概要
Pinia は、ストアを定義する 2 種類の構文をサポートしています。
Option ストア:
- Vue Options API に似ています
- 組み込みの
$reset()メソッドを備えています - 最適な用途:シンプルなユースケース、Vuex に慣れたチーム
Setup ストア:
- Composition API のパターンを使用します
- コンポーザブルとの完全な統合に対応します
- 最適な用途:高度なパターン、ウォッチャーや VueUse との統合が必要な場合
→ 詳細については references/store-syntax-guide.md を読み込んでください: 構文の詳細な比較、例、選択基準
State、Getters、Actions
完全な API リファレンスについては、references/state-getters-actions.md を読み込んでください。
クイックリファレンス
State:
state: () => ({...})(Option)またはref()(Setup)で定義します- 直接アクセスします:
store.count - 直接変更します:
store.count++またはstore.$patch({...}) - リセットします:
store.$reset()(Option ストアのみ)
Getters:
- 算出プロパティです:
getters: { double: (state) => state.count * 2 } thisを使って他の Getters にアクセスします(戻り値の型を指定する必要があります)
Actions:
- ビジネスロジックです:
actions: { increment() { this.count++ } } - 非同期にもできます
- 他のストアに直接アクセスできます
ストアの分割代入:
import { storeToRefs } from 'pinia'
// ✅ For reactivity
const { name, count } = storeToRefs(store)
// ✅ Actions can destructure directly
const { increment } = store
→ 詳細については references/state-getters-actions.md を読み込んでください: API 全般、サブスクリプション、ストア合成パターン、Options API の使用方法
プラグインとコンポーザブル
プラグインとコンポーザブルの詳細なガイドについては、references/plugins-composables.md を読み込んでください。
プラグインの基本
pinia.use(({ store, options }) => {
// Add properties to every store
return { customProperty: 'value' }
})
コンポーザブルとの統合
Option ストア: state() 内での useLocalStorage のような使い方に限定されます
Setup ストア: VueUse/コンポーザブルを完全にサポートします
→ 詳細については references/plugins-composables.md を読み込んでください: プラグインのパターン全般、VueUse との統合、TypeScript の型付け、一般的なパターン(永続化、ルーター、ロガー)
コンポーネント外でのストアの使用
問題点
ストアには Pinia インスタンスが必要です。コンポーネントでは自動的に注入されますが、モジュールスコープでは利用できません。
❌ 誤り:モジュールレベルでストアにアクセスする
// router.ts
import { useUserStore } from '@/stores/user'
// ❌ Fails: Pinia not installed yet
const userStore = useUserStore()
router.beforeEach((to) => {
if (userStore.isLoggedIn) { /* ... */ }
})
✅ 正しい方法:コールバック内でストアにアクセスする
// router.ts
import { useUserStore } from '@/stores/user'
router.beforeEach((to) => {
// ✅ Works: Called after Pinia is installed
const userStore = useUserStore()
if (userStore.isLoggedIn) { /* ... */ }
})
動作する理由:ルーターガードは、app.use(pinia) の完了後に実行されます。
SSR:Pinia インスタンスを明示的に指定する
// server-side
export function setupRouter(pinia) {
router.beforeEach((to) => {
const userStore = useUserStore(pinia) // Pass explicitly
})
}
サーバーサイドレンダリングと Nuxt
SSR と Nuxt の統合に関する完全なガイドについては、references/ssr-and-nuxt.md を読み込んでください。
SSR クイックリファレンス
State のハイドレーション:
- サーバー:
JSON.stringifyではなくdevalue()でシリアライズします - クライアント:
useStore()を呼び出す前にハイドレーションします - 重要:アクション内で
awaitする前に、すべてのuseStore()を呼び出してください
Nuxt 3/4 との統合
bunx nuxi@latest module add pinia
自動インポート: defineStore、storeToRefs、usePinia、acceptHMRUpdate、すべてのストア
→ 詳細については references/ssr-and-nuxt.md を読み込んでください: SSR のパターン全般、Nuxt の設定、サーバーサイドのデータ取得、SSR の落とし穴、デバッグ
テスト
テストの詳細なガイドについては、references/testing-guide.md を読み込んでください。
テストのクイックスタート
import { setActivePinia, createPinia } from 'pinia'
beforeEach(() => {
setActivePinia(createPinia()) // Fresh Pinia for each test
})
コンポーネントのテスト
bun add -d @pinia/testing
import { createTestingPinia } from '@pinia/testing'
mount(Component, {
global: { plugins: [createTestingPinia()] }
})
→ 以下については references/testing-guide.md を読み込んでください: 完全なテストパターン、アクションのスタブ化、ゲッターのモック化、非同期テスト、SSR テスト
ホットモジュールリプレースメント(HMR)
Vite のセットアップ
// stores/counter.ts
import { defineStore, acceptHMRUpdate } from 'pinia'
export const useCounterStore = defineStore('counter', {
// store definition
})
if (import.meta.hot) {
import.meta.hot.accept(acceptHMRUpdate(useCounterStore, import.meta.hot))
}
Webpack のセットアップ
if (import.meta.webpackHot) {
import.meta.webpackHot.accept(acceptHMRUpdate(useCounterStore, import.meta.webpackHot))
}
メリット:
- ページ全体を再読み込みせずにストアを編集できます
- 開発中のアプリケーションの状態を維持できます
- 開発の反復を高速化できます
Options API の使用
Options API を引き続き使用するプロジェクトでは、マッパーの完全なドキュメントを読み込んでください。
→ 以下については references/state-getters-actions.md を読み込んでください: Options API との完全な連携、すべてのマッパー(mapStores、mapState、mapWritableState、mapActions)
Vuex からの移行
移行ガイドの完全版は references/vuex-migration.md を読み込んでください。
すばやく確認する変換の概要
主な変更点:
namespacedを削除します(ストア ID によって自動的に処理されます)mutationsをなくします(状態を直接変更します)commit()を直接の状態変更に置き換えますrootState/rootGettersをストアのインポートに置き換えます- カスタムのクリア用ミューテーションの代わりに
store.$reset()を使用します
ディレクトリ: store/modules/ → stores/(各モジュールが個別のストアになります)
→ 以下については references/vuex-migration.md を読み込んでください: 変換手順の完全版、コンポーネントの移行、チェックリスト、段階的な移行戦略
重要なルール
必ず行うこと
✅ すべての状態プロパティを state() で定義するか、セットアップストアから返します
✅ コンポーネントで状態/ゲッターを分割代入する場合は storeToRefs() を使用します
✅ アプリをマウントする前に app.use(pinia) を呼び出します
✅ セットアップストアからすべての状態を返します(プライベート状態は SSR/DevTools を壊します)
✅ コンポーネント外で使用する場合は、関数/コールバック内で useStore() を呼び出します
✅ 開発時の HMR サポートには acceptHMRUpdate() を使用します
✅ ゲッターが this を使って他のゲッターにアクセスする場合は、戻り値の型を指定します
✅ SSR の状態のシリアライズには devalue を使用します(XSS を防止します)
✅ クライアント側で useStore() を呼び出す前に状態をハイドレートします(SSR)
✅ 非同期アクションでは、await より前にすべての useStore() を呼び出します(SSR)
絶対に行わないこと
❌ ストア作成後に状態プロパティを動的に追加します
❌ storeToRefs() を使わずにストアを直接分割代入します(リアクティビティが失われます)
❌ アクションにアロー関数を使用します(this コンテキストが必要です)
❌ セットアップストアでプライベート状態を返します(SSR/DevTools/プラグインが壊れます)
❌ モジュールのトップレベルで useStore() を呼び出します(Pinia のインストール前)
❌ ストア間に循環依存を作成します(互いの状態を読み取る場合)
❌ SSR のシリアライズに JSON.stringify() を使用します(XSS の脆弱性があります)
❌ アクション内で await の後に useStore() を呼び出します(SSR が壊れます)
❌ this を使う際にゲッターの戻り値の型指定を忘れます
❌ 単体テストで beforeEach(() => setActivePinia(createPinia())) を省略します
既知の問題の予防
このスキルは、文書化された 12 件の問題を防止します。
問題 #1:直接の分割代入によるリアクティビティの喪失
エラー:分割代入後、状態の変更がテンプレートに反映されません
発生する理由:JavaScript の分割代入は Vue のリアクティビティを壊します
予防策:状態/ゲッターには常に storeToRefs() を使用します
問題 #2:状態プロパティを動的に追加できない
エラー:ストア作成後に追加した新しいプロパティがリアクティブになりません
発生する理由:Pinia はリアクティビティのため、すべてのプロパティがあらかじめ定義されている必要があります
予防策:初期値が undefined の場合でも、すべてのプロパティを state() で宣言します
問題 #3:Pinia のインストール前にストアが見つからない
エラー:getActivePinia() が undefined を返します
発生する理由:app.use(pinia) の前に useStore() を呼び出しています
予防策:アプリをマウントする前、またはストアにアクセスする前に app.use(pinia) を呼び出します
問題 #4:セットアップストアのプライベート状態が SSR を壊す
エラー:SSR で状態が正しくシリアライズ/ハイドレートされません
発生する理由:セットアップから返されないプロパティは追跡されません
予防策:セットアップストアからすべての状態プロパティを返します
Issue #5:this を使うゲッターで型が推論されない
エラー:ゲッターが this を使うと、TypeScript が戻り値の型を推論できません
原因:Pinia に関する TypeScript の既知の制限です
予防策:戻り値の型を明示的に指定します:getterName(): ReturnType { ... }
Issue #6:Options API ストアのサフィックスに関する混乱
エラー:コンポーネントで this.counterStore が見つかりません
原因:mapStores() は自動的に 'Store' サフィックスを追加します
予防策:ストア名に 'Store' を付けるか、setMapStoreSuffix() を呼び出します
Issue #7:await の後に呼び出したアクションで SSR が壊れる
エラー:SSR で誤った Pinia インスタンスが使われ、状態が汚染されます
原因:非同期関数内の await によって実行コンテキストが変わります
予防策:await 文の前にすべての useStore() を呼び出します
Issue #8:循環するストア依存関係でアプリがクラッシュする
エラー:Maximum call stack exceeded 原因:初期化中に両方のストアが互いの状態を読み取ります 予防策:セットアップ時の読み取りではなく、クロスストアアクセスにはゲッター/アクションを使います
Issue #9:SSR の状態シリアライズにおける XSS 脆弱性
エラー:状態内のユーザー入力によって悪意のあるスクリプトが実行される可能性があります
原因:JSON.stringify() は実行可能なコードをエスケープしません
予防策:安全なシリアライズには devalue ライブラリを使います
Issue #10:開発時に HMR が機能しない
エラー:ストアを変更するたびにページ全体の再読み込みが必要です
原因:ストア向けに Vite/webpack HMR が設定されていません
予防策:各ストアファイルに acceptHMRUpdate() ブロックを追加します
Issue #11:Composable が返す関数によって Options ストアが壊れる
エラー:ストアの状態にシリアライズできない関数が含まれます
原因:Options ストアの state() が返せるのは書き込み可能な ref のみです
予防策:複雑な composable には setup ストアを使うか、書き込み可能な状態のみを取り出します
Issue #12:ユニットテスト間で状態がリセットされない
エラー:テスト同士が影響し合い、断続的に失敗します
原因:単一の Pinia インスタンスがテスト間で共有されています
予防策:テストスイート内で beforeEach(() => setActivePinia(createPinia())) を使います
パッケージのバージョン(2026-08-03 確認済み)
コア: pinia@^3.0.4、vue@^3.5.24
Nuxt: @pinia/nuxt@^0.11.2、nuxt@^4.0.0(Nuxt 3 は 2026-07-31 に EOL を迎えました。v4 が現在の安定版です)
テスト: @pinia/testing@^1.0.2、vitest@^2.0.0
SSR: devalue@^5.3.2(安全なシリアライズ用)
よく使われるパターン
パターンの完全な例については、リファレンスファイルを参照してください:
- 認証ストア →
references/state-getters-actions.md - 永続化プラグイン →
references/plugins-composables.md - フォームストア →
references/store-syntax-guide.md(setup ストアの例) - ルーターとの統合 →
references/state-getters-actions.md(コンポーネント外でのストアへのアクセス)
公式ドキュメント
- Pinia:https://pinia.vuejs.org/
- はじめに:https://pinia.vuejs.org/getting-started.html
- コアコンセプト:https://pinia.vuejs.org/core-concepts/
- SSR ガイド:https://pinia.vuejs.org/ssr/
- Nuxt との統合:https://pinia.vuejs.org/ssr/nuxt.html
- テスト:https://pinia.vuejs.org/cookbook/testing.html
- Vuex からの移行:https://pinia.vuejs.org/cookbook/migration-vuex.html
- GitHub:https://github.com/vuejs/pinia
トラブルシューティング
問題:"getActivePinia() was called with no active Pinia"
解決策:
- マウント前に
app.use(pinia)が呼び出されていることを確認します - コンポーネント外の場合は、コールバック/関数内で
useStore()を呼び出します - SSR では、Pinia インスタンスを明示的に渡します:
useStore(pinia)
問題:状態の変更がテンプレートに反映されない
解決策:直接分割代入する代わりに storeToRefs() を使います
問題:this を使うゲッターで TypeScript エラーが発生する
解決策:戻り値の型を明示的に指定します:myGetter(): ReturnType { return this.otherGetter }
問題:setup ストアで $reset() を使えない
解決策:手動でカスタムリセットを実装します:
function $reset() {
count.value = 0
name.value = ''
}
return { count, name, $reset }
問題:ストアで HMR が機能しない
解決策:HMR の受け入れブロックを追加します:
if (import.meta.hot) {
import.meta.hot.accept(acceptHMRUpdate(useMyStore, import.meta.hot))
}
問題:テストが断続的に失敗する
解決策:beforeEach() で新しい Pinia を作成します。
beforeEach(() => {
setActivePinia(createPinia())
})
参照資料を読み込むタイミング
次の場合は references/store-syntax-guide.md を読み込んでください:
- Option ストア構文と Setup ストア構文の詳細な比較が必要な場合
- 新しいストアでどちらの構文を使うか決める場合
- Option ストアと Setup ストアのトレードオフについて質問がある場合
- 両方の構文の完全な例が必要な場合
次の場合は references/state-getters-actions.md を読み込んでください:
- state、getters、actions の完全な API リファレンスが必要な場合
$patch、$subscribe、$onActionについて質問がある場合- ストアの合成パターンを実装する場合
- Options API のマッパー(
mapStores、mapState、mapActions)を使う場合 - コンポーネント外(ルーター、プラグイン)からストアにアクセスする場合
次の場合は references/plugins-composables.md を読み込んでください:
- カスタム Pinia プラグインを作成する場合
- VueUse やその他のコンポーザブルをストアに統合する場合
- 永続化、ルーティング、ロギングのプラグインパターンが必要な場合
- プラグインの TypeScript 型指定について質問がある場合
- 高度なコンポーザブル統合を行う場合
次の場合は references/ssr-and-nuxt.md を読み込んでください:
- サーバーサイドレンダリングを設定する場合
- Nuxt 3/4 と統合する場合
- 状態のハイドレーションやシリアライズについて質問がある場合
- SSR 関連のエラー(誤った Pinia インスタンス、ハイドレーションの不一致)が発生した場合
- Nuxt の自動インポートや設定について確認する場合
- サーバーサイドのデータ取得パターンを確認する場合
次の場合は references/testing-guide.md を読み込んでください:
- ストアの単体テストを設定する場合
- Pinia ストアを使用するコンポーネントをテストする場合
- アクションをスタブ化する、またはゲッターをモックする必要がある場合
createTestingPiniaについて質問がある場合- SSR ストアをテストする場合
- Vitest またはテストフレームワークとの統合を行う場合
次の場合は references/vuex-migration.md を読み込んでください:
- 既存の Vuex コードベースを Pinia に移行する場合
- Vuex から Pinia への変換について質問がある場合
- 移行チェックリストや例が必要な場合
- 段階的な移行戦略が必要な場合
セットアップ完全チェックリスト
-
piniaパッケージをインストールした -
createPinia()で Pinia インスタンスを作成した - マウント前に
app.use(pinia)で登録した - ストアディレクトリを作成した(例:
src/stores/) -
defineStore()で少なくとも 1 つのストアを定義した - コンポーネント内で分割代入する際に
storeToRefs()を使用した -
thisを使用する場合、ゲッターの戻り値に型を指定した -
acceptHMRUpdate()で HMR サポートを追加した(開発時) - SSR ハイドレーションを設定した(SSR を使用する場合)
-
@pinia/nuxtを設定した(Nuxt を使用する場合) -
createTestingPinia()でテストを設定した(テストを行う場合) - すべてのストアで一貫した命名規則
use[Name]Storeに従っている - DevTools の統合が機能することを確認した
質問や問題がありますか?
- 公式ドキュメントを確認してください:https://pinia.vuejs.org/
- 上記の「既知の問題の予防」セクションを確認してください
- セットアップチェックリストがすべて完了していることを確認してください
- TypeScript の設定に問題がないか確認してください
- ストアを使用する前に Pinia がインストールされていることを確認してください