Vueでの状態管理ライブラリと言えばVuexだったけど、今やPiniaが公式ライブラリらしい。mutationsなどややこしいものが排除されてシンプル!簡単に実装できそうなのでLaravel9/Vue3環境で使い方を確認してみよう。
目次
開発環境
Windows10
Docker Desktop
WSL2
Laravel9
Laravel Sail
vue v3.2.40
pinia v2.0.23
Piniaのインストール
$ sail npm install pinia
Vue3インスタンスへのPinia組み込み
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './components/App.vue'
const app = createApp(App)
app.use(createPinia())
app.mount('#app')
ストアの作成 defineStore
defineStore(ストア名, プロパティ)
import { defineStore } from 'pinia'
export const useStoreCounter = defineStore('counter', {
// プロパティ設定[state, getters, actions]
…
})
State:状態管理対象
状態管理対象の設定と状態取得をしてみましょう。
Stateの設定
import { defineStore } from 'pinia'
export const useStoreCounter = defineStore('counter', {
state: () => ({
count: 1,
})
})
stateの取得
<template>
<h1>Counter</h1>
<p>Count: {{ counter.count }}</p>
</template>
<script setup>
import { useStoreCounter } from '@/stores/counter'
const counter = useStoreCounter()
</script>
Getters:状態を利用したメソッド
状態(state)を利用して取得するメソッドの作成と利用方法を確かめる。
引数にstateをとることができるのね。
Gettersの設定
import { defineStore } from 'pinia'
export const useStoreCounter = defineStore('counter', {
state: () => ({
count: 100,
}),
getters: {
doubleCount: (state) => state.count * 2,
},
})
Gettersの利用
<template>
<h1>Counter</h1>
<p>Count: {{ counter.count }}</p>
<p>doubleCount: {{ counter.doubleCount }}</p>
</template>
<script setup>
import { useStoreCounter } from '@/stores/counter'
const counter = useStoreCounter()
</script>
Actions:状態変更メソッド
stateを変更するメソッドをactionsに作成
「this」でstateにアクセスするのでアロー関数は使用できないのね。
Actionsの作成
import { defineStore } from 'pinia'
export const useStoreCounter = defineStore('counter', {
state: () => ({
count: 100,
}),
getters: {
doubleCount: (state) => state.count * 2,
},
actions: {
increment() {
this.count++
},
decrement() {
this.count--
},
},
})
Actionsの利用
<template>
<h1>Counter</h1>
<p>Count: {{ counter.count }}</p>
<p>doubleCount: {{ counter.doubleCount }}</p>
<div>
<button @click="counter.increment">++</button>
<button @click="counter.decrement">--</button>
</div>
</template>
<script setup>
import { useStoreCounter } from '@/stores/counter'
const counter = useStoreCounter()
</script>
メソッド
$resetメソッド
stateを初期値にリセット
<button @click="counter.$reset()">Reset</button>
$patchメソッド
stateを任意の値に更新
const patch = () => {
counter.$patch({
count: 100,
})
}
動作テスト
分割代入
Actionsはそのまま分割代入で取得可能。しかし、stateやGettersは取得できないのでstoreToRefs関数を通すこと。
<template>
<h1>Counter</h1>
<p>Count: {{ count }}</p>
<p>doubleCount: {{ doubleCount }}</p>
<div>
<button @click="increment">++</button>
<button @click="decrement">--</button>
</div>
</template>
import { storeToRefs } from 'pinia'
import { useCounterStore } from '@/stores/counter'
const counter = useStoreCounter()
const { increment, decrement } = counter
const { count, doubleCount } = storeToRefs(counter)
CompositionAPIでの書き方
これまでOptionsAPIで記述してきたが、CompositionAPIでも可能。
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
export const useStoreCounter = defineStore('counter', () => {
// State
const count = ref(1)
// Getters
const doubleCount = computed(() => count.value * 2)
// Actions
const increment = () => {
count.value++
}
const decrement = () => {
count.value--
}
return {
count,
doubleCount,
increment,
decrement,
}
})
永続化
別Componentに移動しても、別Componentで取得しても、もちろん同じ値が取得できるが、リロードしたらリセットされてしまう。以下に紹介する永続化プラグインのいずれかを使用することでリロードされても値を保持することが可能。
pinia-plugin-persistedstate
https://github.com/prazdevs/pinia-plugin-persistedstate
インストール
$ sail npm install pinia-plugin-persistedstate
app.js
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
ストアでの設定
import { defineStore } from 'pinia'
export const useStoreCounter = defineStore('conter', {
state: () => {
…
},
persist: true, // 追加
})
pinia-plugin-persist
インストール
$ sail npm install pinia-plugin-persist
app.js
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import piniaPersist from 'pinia-plugin-persist'
const pinia = createPinia()
pinia.use(piniaPersist)
createApp({})
.use(pinia)
.mount('#app')
ストアでの設定
import { defineStore } from 'pinia'
export const useStoreCounter = defineStore('conter', {
state: () => {
…
},
// 追加
persist: {
enabled: true
}
})
vue-routerでPiniaを使用する場合の注意
Piniaをvue-routerのナビゲーションガードで利用する場合、以下のようにbeforeガード内でストアを呼び出す。
import { useUserStore } from '@/stores/user'
…
router.beforeEach((to, from, next) => {
const userStore = useUserStore()
...
}
beforeガードの外で呼び出した場合、以下のようなエラーが出力された。
Uncaught Error: [*]: getActivePinia was called with no active Pinia. Did you forget to install pinia?
const pinia = createPinia()
app.use(pinia)
This will fail in production.
https://pinia.vuejs.org/core-concepts/outside-component-usage.html#single-page-applications