はじめに
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"を追加してください。
{
//中略
"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を追加します。
import { defineNuxtConfig } from "nuxt/config"
// https://v3.nuxtjs.org/api/configuration/nuxt.config
export default defineNuxtConfig({
//中略
//追加
modules: ['@pinia/nuxt'],
});
composables\states.tsを作成し、以下のようにします。composablesディレクトリでは、コンポーネントのスクリプトの一部を別のファイルに切り出して、部品として再利用することができます。
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を作成し、以下のようにします。
<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を作成し、以下のようにします。
<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 を実行すると以下のような画面が表示されます。
まだボタンを押していないので、初期値の0が表示されています。
ここで、「count ページへ飛ぶ」リンクをクリックすると、countページに遷移することができます。そして、count ページに表示されている「+1する」ボタンをクリックすると、countが1ずつインクリメントされます。
以下は「+1する」ボタンを2回押したときの画面です。
「もどる」をクリックすると、こちらにもcount変数が反映されていることが確認できます。
ただし、現時点ではページをリロードするとcount変数が初期値の0に戻ってしまいます。
そこで、pinia-plugin-persistedstateをインストールして、リロードしても状態が保持されるようにしていきます。
サーバーを停止し、以下のコマンドを実行してください。
npm i -D @pinia-plugin-persistedstate/nuxt
インストールしたプラグインが使用できるように、nuxt.config.tsのmodulesにpinia-plugin-persistedstateを追加します。
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を追加して、リロードしても状態が保持されるようにします。
※セッションストレージに保存する場合
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,
},
}
);
※ローカルストレージに保存する場合
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より記述がシンプルで個人的には好きです。
最後までお読みいただき、ありがとうございました!
参考記事