🍍

Pinia 入門

2024/04/03に公開

はじめに

Vue.js でアプリケーションを開発する際、状態管理は避けて通れない課題のひとつです。長らく公式に推奨されてきたのが「Vuex」という状態管理ライブラリでした。しかし、Vuex は柔軟で強力な一方、次第にその設定の複雑さやコード量の多さが課題として挙げられるようになりました。

そんな中、登場したのが Pinia です。Pinia は Vue.js の公式状態管理ライブラリとして位置づけられており、現在は Vue.js の新しいデフォルトの状態管理ライブラリ として推奨されています。Pinia の API は、Vuex 5 RFC(Request for Comments)で提案された仕様とほぼ同等、またはそれ以上に強化されており、Vue 2.x でも利用可能です。

https://pinia.vuejs.org/introduction.html

The official state management library for Vue has changed to Pinia. Pinia has almost the exact same or enhanced API as Vuex 5, described in Vuex 5 RFC. You could simply consider Pinia as Vuex 5 with a different name. Pinia also works with Vue 2.x as well.
(引用元:GitHub - Vuex

なお、Vuex 3 および Vuex 4 は引き続き保守されますが、新しい機能が追加される可能性は低いとされています。もし新規プロジェクトを始める場合、Pinia を使用することを公式も強く推奨しています。一方、既存の Vuex アプリを Pinia に移行する際には、Vuex と Pinia を同じプロジェクト内で共存させながら移行を進める方法も選択肢として考えられるでしょう。

本記事では、以下のような方を対象に、Pinia の基本的な使い方から Vuex との違い、さらに Vuex から Pinia への移行方法までを解説していきます。

  • Vue.js の状態管理ライブラリとして Pinia を使いたい方
  • Pinia の基本的な使い方を知りたい方
  • Vuex を使っていて Pinia への移行を考えている方

Pinia を導入することで、Vue.js の開発体験がどのように変わるのか、一緒に見ていきましょう!

Pinia の概要

Pinia とは?

Pinia は Vue.js の公式状態管理ライブラリとして認定されており、アプリケーション全体で状態(state)を共有・管理するために設計されたツールです。Vue 2.x と Vue 3 の両方をサポートしており、特にサーバーサイドレンダリング(SSR)や大規模アプリケーションの開発にも対応しています。

Pinia の大きな魅力は、そのシンプルで直感的な API と多彩な開発支援機能にあります。以下に、Pinia を選ぶべき理由を具体的に挙げていきます。

Pinia の特長

  1. 開発体験の向上

    • Devtools サポート:Vue DevTools と統合されており、状態やアクションのタイムラインを視覚的に追跡可能。
    • タイムトラベル:過去の状態に戻る「タイムトラベル機能」を使い、デバッグが容易に。
    • ホットモジュールリプレースメント(HMR):開発中に状態を保持したままストアを更新可能で、効率的なコーディングが可能。
  2. 機能の拡張性

    • プラグインの利用:Pinia の機能を自由に拡張可能で、アプリケーションのニーズに応じたカスタマイズが可能。
    • ストアの可視性:Pinia ストアは使用されているコンポーネントに直接表示され、追跡が簡単。
  3. 多様なサポート機能

    • テストツール:Pinia はテストユーティリティを備えており、ストアのロジックや動作を簡単に検証可能。
    • サーバーサイドレンダリング(SSR)対応:SSR 環境でも安全かつ効率的に状態管理を行える。
  4. 型安全性

    • TypeScript との親和性:型推論やオートコンプリートが強力で、JavaScript ユーザーにとってもコード補完が使いやすい。

Pinia は、これらの機能を通じて、Vue.js アプリケーションの開発効率とデバッグ体験を大幅に向上させます。

Pinia を使ってみる

Pinia の概要を理解したところで、実際に Pinia を使ってみましょう。ここでは、カウンターアプリを作成する流れを例に、Pinia の基本的なセットアップとストアの作成方法を解説します。

事前に Vue 3 のプロジェクトを作成していることを前提とします(参考:Vue CLI を使ったプロジェクト作成方法)。

手元でサクッと試したい方は、以下の StackBlitz プロジェクトをご覧ください。

https://stackblitz.com/edit/vitejs-vite-udeybrke?file=src%2Fstores%2Fcounter.ts

Pinia のインストール

まず、Pinia をインストールします。以下のコマンドを実行してください。

pnpm install pinia

または、npm/yarn を使用してインストールすることも可能です。

npm install pinia
# または
yarn add pinia

Pinia の初期化

次に、Pinia をアプリケーションに組み込みます。main.ts ファイルに以下のように記述します。

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

// Vue アプリケーションを作成
const app = createApp(App)

// Pinia を作成してアプリケーションに登録
const pinia = createPinia()
app.use(pinia)

// アプリケーションをマウント
app.mount('#app')

Pinia を Vue アプリケーション全体で利用できる状態にするため、createPinia を呼び出してアプリに登録しています。

ストアの作成

次に、カウンターの状態を管理するストアを作成します。Pinia では、defineStore を使用してストアを定義します。

// stores/counter.ts
import { defineStore } from 'pinia';

interface CounterState {
  count: number;
}

export const useCounterStore = defineStore('counter', {
  // 初期状態を定義
  state: (): CounterState => ({
    count: 0,
  }),
  // アクション(状態変更を定義)
  actions: {
    increment() {
      this.count++;
    },
    decrement() {
      this.count--;
    },
    reset() {
      this.count = 0;
    },
  },
});

上記のコードは、以下の役割を持っています:

  • state: 管理したい状態を定義。ここでは count を初期値 0 で定義しています。
  • actions: 状態を変更するための関数を定義。例として、カウントを増減する incrementdecrement、リセットする reset を実装しています。

Pinia Store の動作確認

これで簡易的なカウンターストアが完成しました!
ストアを使うには、コンポーネント内で以下のように呼び出します:

// App.vue
<script setup lang="ts">
import { useCounterStore } from './stores/counter';

const counter = useCounterStore();

const increment = () => counter.increment();
const decrement = () => counter.decrement();
const reset = () => counter.reset();
</script>

<template>
  <div class="counter">
    <button @click="decrement">-</button>
    <span>Count: {{ counter.count }}</span>
    <button @click="increment">+</button>
    <button @click="reset" class="reset">Reset</button>
  </div>
</template>

ボタンをクリックして、+- を押すとカウントが変化します。リセットボタンを押すと、カウントが 0 に戻ります。

このように、Pinia を使うことで、Vue.js アプリケーションの状態管理がよりシンプルで直感的になります。

Vuex で実装した場合

Pinia の基本的な使い方を知ったところで、Vuex の場合と比較してみましょう。

手元でサクッと試したい方は、以下の StackBlitz プロジェクトをご覧ください。

https://stackblitz.com/edit/vitejs-vite-ekgku3p2?file=src%2Fmain.ts

Vuex のインストール

Vuex をインストールします。以下のコマンドを実行してください。

pnpm install vuex@next

または、npm/yarn を使用してインストールすることも可能です。

npm install vuex@next
# または
yarn add vuex@next

Vuex の初期化

import { createApp } from 'vue'
import App from './App.vue'
import store from './store'

const app = createApp(App)
app.use(store)
app.mount('#app')

Vuex Store の作成

// store/index.ts
import { createStore } from 'vuex';

interface State {
  count: number;
}

export default createStore({
  state: {
    count: 0,
  },
  mutations: {
    increment(state: State) {
      state.count++;
    },
    decrement(state: State) {
      state.count--;
    },
    reset(state: State) {
      state.count = 0;
    },
  },
  actions: {
    increment({ commit }) {
      commit('increment');
    },
    decrement({ commit }) {
      commit('decrement');
    },
    reset({ commit }) {
      commit('reset');
    },
  },
  modules: {},
});

Vuex Store の動作確認

// App.vue
<script setup lang="ts">
import { useStore } from 'vuex';
import { computed } from 'vue';
import { State } from './store/types';

const store = useStore<State>();

const count = computed(() => store.state.count);

const increment = () => store.dispatch('increment');
const decrement = () => store.dispatch('decrement');
const reset = () => store.dispatch('reset');
</script>

<template>
  <div class="counter">
    <button @click="decrement">-</button>
    <span>Count: {{ count }}</span>
    <button @click="increment">+</button>
    <button @click="reset" class="reset">Reset</button>
  </div>
</template>

Vuex との違い

Vuex のコード例を見てみると、Vuex と Pinia には多くの共通点がありますが、API の設計や状態変更の流れにいくつか重要な違いがあります。

状態変更のワークフローの違い

  • Vuex: コンポーネント → アクション(任意) → ミューテーション → 状態変更
  • Pinia: コンポーネント → アクション → 状態変更

Vuex では、状態を変更するために必ず ミューテーション を経由する必要があります。一方、Pinia ではミューテーションを廃止し、アクションから直接状態を変更する設計になっています。

Mutation が廃止された背景

Pinia の開発者は、以下のように述べています。

After many years using Vuex, most mutations were completely unnecessary as they were merely doing a single operation via an assignment (=) or collection methods like push.

https://github.com/vuejs/pinia/issues/58

Vuex のミューテーションは、単純な状態変更(例: state.count++)がほとんどで、その冗長性を解消するために廃止されました。この変更により、コードが簡潔になり、開発者の負担が軽減 されます。

Pinia では、ミューテーションなしでも DevTools による状態追跡が可能なため、デバッグ体験が損なわれることもありません。

ディレクトリ構造の違い

Pinia と Vuex では、ディレクトリ構造にも違いがあります。

Vuex の例(モジュール構造)

src
└── store
    ├── index.ts         # Vuex を初期化
    └── modules
        ├── module1.ts   # モジュール1
        ├── module2.ts   # モジュール2

Pinia の例(ストア構造)

src
└── stores
    ├── counter.ts       # Counter ストア
    ├── module1.ts       # モジュール1を独立したストアとして移行
    ├── module2.ts       # モジュール2を独立したストアとして移行

Pinia は 「複数のストアを持つ設計」 になっているため、stores というディレクトリ名が一般的です。Vuex のモジュールは、Pinia ではそれぞれ独立したストアとして再構成されます。

まとめ

本記事では、Vue.js の公式状態管理ライブラリである Pinia の基本的な使い方から、Vuex との違い、そして移行のポイントまでを解説しました。

Pinia の大きな特長は、以下の点にあります:

  • シンプルで直感的な API により、開発体験が大幅に向上する。
  • ミューテーションの廃止 によるコードの簡潔化。
  • TypeScript との親和性 で型安全な開発が可能。
  • Vue DevTools との統合 による高度なデバッグ機能。

また、Vuex から Pinia への移行を考えている方は、公式ドキュメントの以下のページを参考にしてください:

https://pinia.vuejs.org/cookbook/migration-vuex.html#Restructuring-Modules-to-Stores

Pinia を導入することで、Vue.js アプリケーションの状態管理がどれだけ効率的になるかを、ぜひ体験してみてください。Vue.js の開発がさらに快適で楽しいものになるはずです。

GitHubで編集を提案
Vue・Nuxt 情報が集まる広場 / Plaza for Vue・Nuxt.

Discussion