トップページへ

S.A.G.A佐賀!!出身者のプログラミングブログ

VueのストアライブラリであるPinia(ピーニャ)を使った状態管理

VueのストアライブラリであるPinia(ピーニャ)を使った状態管理

現在Vueが推奨している状態管理のライブラリはPiniaとなっています。 以前はVuexがよく使用されていたと思いますが、新しいプロジェクトで状態管理ライブラリを使用するとなった場合はPiniaが選択されるかと思います。 今回は公式が推奨しているPiniaでの状態管理の方法解説します。 ストアの書き方にはOptions APIと同様な記述をするOption StoresとComposition APIのsetup関数と同様な記述をするSetup Storesがあります。 私が初めてVueを使用したときには、既にSetup関数があったので慣れもあり、Setup Storesの方で記述したいと思います。

プロジェクトの作成

まずはプロジェクトの作成を行いたいと思います。 私が今回使用したNode.jsとNpmのバージョンは下記になります。

  • Node.js 20.10.0
  • Npm 10.2.3

私は下記コマンドを流しプロジェクトを作成しますが、Vueのプロジェクトを作れれば何でも構いません。 ご自身で用意されたプロジェクトの場合、適宜読み替えてください。

npm create vue@latest

上記のコマンドを流すといくつか質問されます。 ここでAdd Pinia for state management?をYesにするとプロジェクト作成時にPiniaもインストールできますが、他のVue環境でもPiniaを導入できるようにインストールからやってみるためNoにしてください。

Project name: ... project-name
√ Add TypeScript? ... Yes
√ Add JSX Support? ... No
√ Add Vue Router for Single Page Application development? ... No
√ Add Pinia for state management? ... No
√ Add Vitest for Unit Testing? ... No
√ Add an End-to-End Testing Solution? » No
√ Add ESLint for code quality? ... Yes
√ Add Prettier for code formatting? ... Yes
√ Add Vue DevTools 7 extension for debugging? (experimental) ... Yes

作ったプロジェクトを開き、パッケージをアップデートします。 私はnpm-check-updatesをグローバルインストールしているので下記コマンドで任意のパッケージを最新にします。

ncu -i

下記コマンドを流してモジュールをインストールします。

npm i

Piniaをインストールします。

npm i pinia

各パッケージのバージョンは以下になります。

  • Vue 3.4.29
  • Pinia 2.1.7
  • TypeScript 5.4.5

Piniaの読み込み

Piniaを使えるようにするために、main.tsでPiniaのインスタンスを作成し、appへ渡します。 これでPiniaを利用することができます。

src/main.ts

import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'

const app = createApp(App)
// Piniaのインスタンスを作成
app.use(createPinia())

app.mount('#app')

ストアの定義

srcフォルダ直下にstoresフォルダを作成します。 今回はカウンターに関する処理なのでcounter.tsファイルを作成します。 ストアの変数名はuse〇〇Storeと命名します。 この命名は公式が推奨している命名のようです。 今回の場合はカウンターの処理なのでuseCounterStoreとします。 defineStore()の第一引数(id)は一意の値を渡します。 この値はVue DevToolsに接続するために使用するようです。 Vue DevToolsはプロジェクト作成時にインストールしています。

src/stores/counter.ts

import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', () => {})

状態を持つcountとcountの値を倍にするdoubleCount、呼び出される毎にcountを+1するincrementを用意します。 Piniaのストアにはstate, getters, actionsの3つの概念があり、Options APIでいうdate, computed, methodsにあたります。 そして必ず全てのプロパティをreturnする必要があります。

src/stores/counter.ts

import { ref, computed } from 'vue'
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', () => {
  // state
  // カウントの初期値は0
  const count = ref(0)
  // getter
  // 現在のカウントを2倍
  const doubleCount = computed(() => count.value * 2)
  // action
  // 1ずつカウントアップ
  function increment(): void {
    count.value++
  }

  return { count, doubleCount, increment }
})

またstateをリセットする場合、Option Storesで記述すると$reset()を呼び出すだけでリセットできるのですが、Setup Storesの場合は独自に$reset()メソッドを作成する必要があります。

src/stores/counter.ts

import { ref, computed } from 'vue'
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', () => {
  const count = ref(0)
  const doubleCount = computed(() => count.value * 2)
  function increment(): void {
    count.value++
  }
  // リセットメソッド作成
   function $reset(): void {
    count.value = 0
  }

  return { count, doubleCount, increment, $reset }
})

ストアの利用

前項で作成したストアを実際に利用するためのコンポーネントを作成します。 storeを読み込み変数に代入します。

src/components/CounterMain.vue

<script setup lang="ts">
import { useCounterStore } from '@/stores/counter'

const counterStore = useCounterStore()
</script>

そのまま読み込むとリアクティブでなくなるためPiniaに用意されているstoreToRefs()を使用し分割代入で読み込みます。 メソッドに関してはそのまま分割代入で読み込んで大丈夫です。

src/components/CounterMain.vue

<script setup lang="ts">
import { storeToRefs } from 'pinia'

import { useCounterStore } from '@/stores/counter'

const counterStore = useCounterStore()
const { count, doubleCount } = storeToRefs(counterStore)
const { increment, $reset } = counterStore
</script>

<template>
  <div>カウント{{ count }}</div>
  <div>↑を2倍した数値{{ doubleCount }}</div>
  <button @click.stop="increment">+1</button>
  <button @click.stop="$reset">リセット</button>
</template>

ここまでで作成したコンポーネントを起点となるファイルで読み込んであげましょう。 実際にローカル環境で立ち上げボタンをクリックするとカウンターが動くはずです。

src/App.vue

<script setup lang="ts">
import CounterMain from '@/components/CounterMain.vue'
</script>

<template>
  <CounterMain />
</template>

また本記事と同じnpm create vue@latestコマンドでプロジェクトを作成し、Add Vue DevTools 7 extension for debugging?をYesにしているとローカル環境を立ち上げた際にストアの中身を確認することができます。

Vue DevToolsでストアを確認している様子

簡単なサンプルでしたがPiniaの明快さや使い方は伝わりましたでしょうか。 公式ドキュメントにも複数のサンプルが掲載されているので興味が沸いたら覗いてみてください。