https://pinia.vuejs.org/
Vuexとの違いについて
現状のVuex (Vuex3.x/4.x)とは公式によると下記のような違いがあります。
・mutationの廃止
・Typescriptのサポート
・ネストされたモジュールの廃止
・ネームスペースの廃止
などなどです。
試してみる
create-vueで作成したプロジェクトの中にあるCounterStoreを使っていろいろと試してみます。
記法
現在のVuexでは、state・mutation・getters・actionsと区分が分かれており、Options APIのような記法ですが、PiniaはComposition APIのような形でも書くことができます。
stores/counter.js
import { defineStore } from "pinia";
import { ref } from "vue";
export const useCounterStore = defineStore({
id: 'counter',
state: () => ({
counter: 0
}),
getters: {
doubleCount: (state) => state.counter * 2
},
actions: {
increment() {
this.counter++
}
}
})
export const useCounterStore = defineStore("counter", () => {
const count = ref(0);
const doubleCount = () => {
return count.value * 2;
};
const increment = () => {
count.value++;
};
return {
count,
increment,
doubleCount,
};
});
import { useStore } from "vuex"
const store = useStore();
store.state.count;
store.dispatch("increment");
store.getters.doubleCount();
import { useCounterStore } from "./stores/counter"
const store = useCounterStore();
store.count;
store.increment();
store.doubleCount();
ネームスペースが廃止されたことで呼び出しの際もシンプルになりました。
Vuex側は呼び出しが長くなる傾向がありましたが、Piniaのように分割されていれば、その心配もなく、わかりやすいと思います。
他のstoreを使用する場合
Vuexではモジュールから親を呼び出す際にRootStateやRootGettersを呼び出して使用していましたが、Piniaではstore自体が分かれているため、実装も少し異なります。
例えば、ユーザー情報を使用するような例だとこのような書き方ができます。
export const useUserStore = defineStore('user', () => {
const users = ref([{
id: 1,
firstname: "Taro",
lastname: "Yamada"
}])
return { user }
});
export const useStore = defineStore('main', () => {
const getUsers = () => {
const userStore = useUserStore()
return userStore.users
};
return { getUsers }
});
例としてはあまり良いものでなくて申し訳ないのですが、他の例だと、APIリクエスト時にtokenを別のstoreから持ってくるとかだとわかりやすいと思います。
共通のメソッド
Piniaでは、mutationが廃止された代わりにsubscribeやonActionなどの便利なメソッドがあります。その中のいくつかを紹介します。
$patch
Vuexのmutationのようなものです。
stateの変数を1つずつではなく、いくつかまとめて変更する際に使用します。
オブジェクトで変更する形式と関数を使って変更する2パターンがあります。
const store = useCounterStore();
store.$patch({
counter: store.counter + 1,
name: 'Abalam',
})
cartStore.$patch((state) => {
state.items.push({ name: 'shoes', quantity: 1 })
state.hasChanged = true
})
$subscribe
stateの変更を監視するメソッドです。
公式ではここで、localStorageにstateを保存する例が書かれていました。
const store = useCounterStore();
store.$subscribe((mutation, state) => {
console.log(mutation, state);
});
$onAction
actionsの実行を追跡します。
loadingの処理などにも使えそうですし、使い道が多そうです。
store.$onAction(
({
name,
store,
args,
after,
onError,
}) => {
const startTime = Date.now()
console.log(`Start "${name}" with params [${args.join(', ')}].`)
after((result) => {
console.log(
`Finished "${name}" after ${
Date.now() - startTime
}ms.\nResult: ${result}.`
)
})
onError((error) => {
console.warn(
`Failed "${name}" after ${Date.now() - startTime}ms.\nError: ${error}.`
)
})
}
)
$subscribe, $onActionはデフォルトではunmounted時に追跡が解除されますが、それぞれの第二引数が設定値(デフォルトはfalse)となっていますので、trueにすればunmounted以降も追跡されます。
このほかにもいろいろなメソッドがありますので、気になる方は公式ドキュメントを見てみてください。
感想
使ってみた感想として、mutationがないというのが少し違和感を感じたり、VuexでいうところのRootStateや他のモジュールを使用したいとなったときに結局、そのstore内で別のstoreを呼び出さないといけないので、その部分には慣れない部分があります。
Vueでフロントエンドに入ったので、Vuex以外の状態管理をあまり触ってこなかったのが理由かもしれません。
ただ、Vuexと比較してもsubscribeやonActionなど便利な機能は多いので今後は当分Piniaを使ってみようかなと思います。