LoginSignup
7
11

Nuxt3でPiniaを使って状態管理を実装してみた

Posted at

はじめに

Nuxt3でPiniaを使って状態管理を実装してみました。

PiniaはVueで扱うグローバルなデータの状態を管理するための状態管理ツールです。Vue2まではVuexがデフォルトのVueの状態管理ツールでしたが、Vue3ではPiniaの利用が推奨されています。
この記事では、Piniaを使用してファイル間での状態共有を実現し、さらに pinia-plugin-persistedstate/nuxt プラグインを利用して、ページをリロードしても状態が保持される機能を実装します。

開発環境

  • Windows 11
  • Nuxt.js 3.4.1
  • Vuetify 3.4.0-alpha.0
  • npm 8.19.4
  • Node.js 16.20.0

ディレクトリ構造

本記事で使用するプロジェクトのディレクトリ構造です。ファイルの作成については、実装を進めながら必要に応じて行いますので、現時点では作成されなくても構いません。

[プロジェクト名]/
├── composables/
│   └── states.ts     
├── pages/
│   ├── index.vue      
│   └── count.vue     
├── plugins/
│   └── vuetify.ts  
├── nuxt.config.js 
└── package.json     

実装

Nuxt3プロジェクトを作成します。

npx create-nuxt-app [プロジェクト名]

  
作成したプロジェクトのディレクトリに移動します。

cd nuxt-test

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

npm i pinia @pinia/nuxt

  
公式によると、npmでインストールした場合、依存関係のエラーが発生することがあるようです。私は発生しました。その場合は、package.jsonにVueの"overrides"を追加してください。

package.json
{
  //中略

  "devDependencies": {
    "@pinia-plugin-persistedstate/nuxt": "^1.1.1",
    "mdi": "^2.2.43",
    "nuxt": "^3.4.1",
    "sass": "^1.66.1",
    "vuetify": "^3.4.0-alpha.1"
  },
  "dependencies": {
    "@pinia/nuxt": "^0.4.11",
    "pinia": "^2.1.6"
  },
	//以下を追加
  "overrides": {
    "vue": "latest" 
  }
}

  
次に、Piniaが使用できるように、nuxt.config.tsのmodulesにPiniaを追加します。

nuxt.config.ts
import { defineNuxtConfig } from "nuxt/config"

// https://v3.nuxtjs.org/api/configuration/nuxt.config
export default defineNuxtConfig({
 
	//中略

 //追加
  modules: ['@pinia/nuxt'],
});

  
composables\states.tsを作成し、以下のようにします。composablesディレクトリでは、コンポーネントのスクリプトの一部を別のファイルに切り出して、部品として再利用することができます。

composables\states.ts
import { ref, computed } from "vue";
import { defineStore } from "pinia";

export const useCounterStore = defineStore(
    "counter",
    () => {
        const count = ref(0);
        function increment() {
            count.value++;
        }
        return { count, increment };
    },
);

defineStore関数はPiniaライブラリから提供され、ストアを定義するために使用されます。ストアはアプリケーションの状態管理を行うために使用され、ファイル間でデータを共有できます。
ストアの定義部分では、countと、countを増やすためのincrement関数を定義しています。
  
次に、pages\index.vueを作成し、以下のようにします。

pages\index.vue
<script setup>
const counter = useCounterStore();
</script>

<template>
  <v-container>
    <nuxt-link to="/count">count ページへ飛ぶ </nuxt-link> //countページに遷移する
    <p>ボタンを{{ counter.count }}回おしました!</p>
  </v-container>
</template>

scriptタグ内でcomposables\states.ts内のuseCounterStoreを呼び出し、その中のcount変数を表示させています。
countページはこの後作成します。

次に、countページにあたるpages\count.vueを作成し、以下のようにします。

pages\count.vue
<script setup>
import { storeToRefs } from "pinia";
const counterStore = useCounterStore();
const { increment } = counterStore;
const { count } = storeToRefs(counterStore);
</script>

<template>
    <v-container>
        <h1>Count:{{ count }}</h1>
        <v-btn @click="increment">+1する</v-btn>
        <nuxt-link to="/" class="ml-4">もどる</nuxt-link>
    </v-container>
</template>

「+1する」ボタンを押すと、composables\states.ts内のuseCounterStoreで定義したincrement関数が呼び出され、ボタンを押すごとにcount変数が1ずつインクリメントされる仕組みです。
  
コマンドで npm run dev を実行すると以下のような画面が表示されます。

Untitled (2).png 
まだボタンを押していないので、初期値の0が表示されています。

ここで、「count ページへ飛ぶ」リンクをクリックすると、countページに遷移することができます。そして、count ページに表示されている「+1する」ボタンをクリックすると、countが1ずつインクリメントされます。
以下は「+1する」ボタンを2回押したときの画面です。
Untitled (11).png

「もどる」をクリックすると、こちらにもcount変数が反映されていることが確認できます。
Untitled (12).png

ただし、現時点ではページをリロードするとcount変数が初期値の0に戻ってしまいます。
そこで、pinia-plugin-persistedstateをインストールして、リロードしても状態が保持されるようにしていきます。

サーバーを停止し、以下のコマンドを実行してください。

npm i -D @pinia-plugin-persistedstate/nuxt

  
インストールしたプラグインが使用できるように、nuxt.config.tsのmodulesにpinia-plugin-persistedstateを追加します。

nuxt.config.ts
import { defineNuxtConfig } from "nuxt/config"

// https://v3.nuxtjs.org/api/configuration/nuxt.config
export default defineNuxtConfig({
 
	//中略

  modules: [
        '@pinia/nuxt',
        '@pinia-plugin-persistedstate/nuxt' //追加
    ],
});

  
composables\states.tsにpersistを追加して、リロードしても状態が保持されるようにします。
  
※セッションストレージに保存する場合

composables\states.ts
import { ref, computed } from "vue";
import { defineStore } from "pinia";

export const useCounterStore = defineStore(
    "counter",
    () => {
        const count = ref(0);
        function increment() {
            count.value++;
        }
        return { count, increment };
    },

		//persistを追加して状態を保持する
    {
        persist: {
            //セッションストレージに保存する場合
            storage: persistedState.sessionStorage,
        },
    }
);

  
※ローカルストレージに保存する場合

composables\states.ts
import { ref, computed } from "vue";
import { defineStore } from "pinia";

export const useCounterStore = defineStore(
    "counter",
    () => {
        const count = ref(0);
        function increment() {
            count.value++;
        }
        return { count, increment };
    },

		//persistを追加して状態を保持する
    {
        persist: {
            //ローカルストレージに保存する場合
            storage: persistedState.localStorage,
        },
    }
);

セッションストレージに保存する場合、ブラウザやタブを閉じると値が破棄されます。
ローカルストレージに保存する場合、キャッシュクリアを明示的に行わない限り、ブラウザやタブを閉じても状態は保持されます。
どちらに保存するかは、目的に合わせて選んでください。
状態を永久に保持したい場合は、別途データベースなどを構築する必要があります。

  
npm run dev でサーバーを立ち上げ、先ほどと同じようにインクリメントを実行した後、リロードしてみてください。リロードしても状態が保持されていれば成功です。

終わりに

今回は、Nuxt3でPiniaを使って状態管理を実装してみました。
Vuexより記述がシンプルで個人的には好きです。
最後までお読みいただき、ありがとうございました!

参考記事

7
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
11