ステート管理?
Vueプロジェクトでステート管理していますか?
小規模なプロジェクトであればpropsやemitを使用して親子間の値の受け渡しや関数の実行ができるかもしれません。
例えば、親と子の間程度であればpropsでの値の受け渡しでも管理が煩雑になることは少ないかもしれません。
<template>
<div>
<Child :data="data" :datas="datas" />
</div>
</template>
<style></style>
<script setup lang="ts">
import Child from './child.vue'
import { reactive, ref } from 'vue'
interface DataType {
data1: string
data2: string
}
const data = ref<string>('data')
const datas = reactive<DataType>({
data1: 'data1',
data2: 'data2'
})
</script>
<template>
<div>
<div>
<div>data</div>
<div>{{ props.data }}</div>
</div>
<div>
<div>data1</div>
<div>{{ props.datas.data1 }}</div>
<div>data2</div>
<div>{{ props.datas.data2 }}</div>
</div>
</div>
</template>
<style></style>
<script setup lang="ts">
interface DataType {
data1: string
data2: string
}
const props = defineProps<{ data: string; datas: DataType }>()
</script>
しかし、孫コンポーネントまで値を渡したいとなった際など、propsでバケツリレーを行うことになり値の管理が煩雑になってきます。
また、コンポーネントの階層を重ねなくても、複数のコンポーネントで同じ値を使用したいときなどにもpropsを多様することなります。
propsやemitを使用せずに同じ値を使用したいといった際に使用するのがストアと呼ばれるステート管理を行ってくれるライブラリです。
Vue2で推奨されていたVuex
Vue2では、Vuexと呼ばれるストアが公式で推奨されていました。
Vuexでは、state(データの管理部分), mutations(stateの状態を変更。stateの変更はmutationsを通して行う), actions(mutationsをコミットして状態を変更させる、非同期処理を行う), getters(stateの内容を処理したあとに取得)といった概念があり、Vuexでのステート管理には、これらの概念を理解することから始まるので学習コストがかかります。
また、JavaScript向きに開発されているため、TypeScriptだと記述し辛いです。
import { createStore } from 'vuex'
import axios from './axios'
export interface State {
data: string
datas: {
data1: string
data2: string
}
}
export const store = createStore<State>({
// データ
state: {
data: '',
datas: {
data1: '',
data2: ''
}
},
// stateの操作
mutations: {
setData(state, data: string) {
state.data = data
},
setDatas(state, datas: { data1: string; data2: string }) {
state.datasn = datas
}
},
// 非同期操作, mutationの呼び出し
actions: {
// データの取得
async getData({ commit }) {
try {
const result = await axios.get('/api/data', { timeout: 5000 })
if (result && result.data) {
// 取得結果が存在する場合は、mutation経由でstateを更新
commit('setData', result.data)
}
} catch (error) {
console.error(error)
throw new Error(error as string)
}
}
}
})
Vue3で推奨されているPinia
Vue3では、Vuexに変わりPiniaが公式で推奨されるようになりました。
Piniaでは、TypeScriptのフルサポート、Vuexであったmutationsの廃止などが行われ、Vuexに比べストアの処理が記述しやすくなりました。
Vue3プロジェクトの作成時にPiniaを選んだときに作成されたcounter.tsの処理を見ると、Piniaのシンプルさがわかるかと思います。
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() {
count.value++
}
return { count, doubleCount, increment }
})
Vuexのようにstate, mutations, actionsといった記述がなくなり、リアクティブ化した変数で値を管理したり、インクリメント関数を使用して、リアクティブ化した変数の値を変更するといった通常のTypeScriptの処理でストアを使用することができます。
Piniaでは、mutationsはなくなりましたが、state, actions, gettersを使用することもできるので、Vuexのコードを移植する際にも、少ない変更でPiniaに以降することができます。
実際に、Vue3プロジェクトでPiniaを使用したサンプルコードとして、以下をご覧ください。
vue3-pinia
さいごに
ストアライブラリのPiniaは、Vuexに比べシンプルでわかりやすいため、今までPropsやEmitのバケツリレーで値の受け渡しを行っていたプロジェクトにも比較的用意に追加することができるのではないかと思います。
Vue3とPiniaで楽々ステート管理を行っていきましょう!