Nuxt3移行のついでにVuexからPiniaに切り替えた話
この記事はラクスパートナーズAdventCalendar2023の21日目の記事です。
はじめに
私の所属するチームではNuxtを使用しています。
NuxtはVue.jsのフレームワークで、2022年の11月に、Nuxt3がリリースされました。
Nuxt2はVue2に依存しており、Vue2は2023年12月でEOLを迎えることから、EOLを迎える前に移行を完了させることが望ましく、Nuxt3への移行は優先度高の案件でした。
ありがたいことに、Nuxt3移行を担当させていただきまして、2023年7月に移行を完了することできました🎉
今回は移行作業の1つでしたPiniaに関して書かせていただきます。
この記事の対象者
- Nuxt3移行に関心がある方
- NuxtでPiniaを導入したい方
なぜVuexからPiniaに切り替えた?
Vueのエコスシテムにおける、現在の状態管理ライブラリの推奨はPiniaだからです。
なぜNuxt3移行のついでとなった?
Nuxt3ではVuexとの統合を提供しなくなったので、移行作業が必要になりました。
Vuexを継続して使う場合も移行作業は必須ですので、それならこれを機にPiniaに切り替えてしまおうと考え、Nuxt3移行のついでに導入することにしました。
Piniaの導入方針
以下の2点がポイントになりました。
- Nuxt2で導入する
- Pinia導入前に既存のstoreをリファクタする
Nuxt2で導入する
PiniaはNuxt3移行時に導入することも可能なのですが、以下の2つを理由に、Nuxt2で導入することにしました。
① 3系(Nuxt・Vue)への移行は、破壊的変更が多い
これは3系への移行において、とても重要なポイントです。
この破壊的変更の多さを嫌って、ReactやNextにリプレイスするというケースも少なくありません。
3系に移行する場合、まず2.7系を経由して、Composition APIへの書き換えなど、3系移行前に対応可能な作業はやり切ってしまうというのが、3系移行における基本戦略と思っています。
それでも、3系移行時のビックバンリリースは避けられませんが、この作業があることで、移行時の劣化リスクを大幅に減らすことできます。
PiniaはNuxt2から使えるので、3系に移行する前に導入が可能でした。
② Pinia導入済みなら、Nuxt3への移行作業がほぼない
Nuxt3への移行に詳細がありますが、Nuxt3移行時にはほぼやることがなかったです。
この2つを理由に、Nuxt2で導入する方が明らかにメリットがあったので、Nuxt2で導入することにしました。
Pinia導入前に既存のstoreをリファクタする
既存のstoreには大量の負債がありまして、そのままPiniaを導入するのは怖さがありました。
また、新規作成したPiniaのstoreファイルに、わざわざ負債のコードを書くのも不毛な作業と思いまして、これを機に一掃することにしました。
リファクタ
既存のstoreには以下の負債がありました。
- 使っていないstate・actions・gettersが大量にある
- storeで管理する必要がない
- actions・gettersのコード量と数が多すぎる
storeで管理する必要がない
特定のVueコンポーネントやComposablesで定義すれば良いコードがstoreに書かれていました。
やることはシンプルで、特定のVueコンポーネントやComposablesにコードを移動するだけです。
移動した結果、storeファイル自体削除可能なケースもあり、Pinia導入のコスト削減になりました。
actions・gettersのコード量と数が多すぎる
storeにロジックを書くということは、storeに依存する呼出元の要求を抱えることになります。
要求が揃うなら特に問題にはなりませんが、もちろん揃わないケースもあり、以下のようなことが繰り返し起きていました。
- 特定の呼出元の要求を満たすため、actionsやgettersに、if分を書いて対応する
- だんだん、コード量が増えてきて危機感を覚える
- たまらず、新しいactions・gettersを作成する
storeに限らずですが、このようなコードを見たことがあるのではないでしょうか?
これの繰り返しにより、storeがカオスになってしまいました。
この負債に対処するため、私のチームではstoreにロジックを書くのを基本NGにしました。
つまり、storeでできることは、stateの参照と更新だけです。
なので、actionsとgettersのコードは基本全て移動しました。
具体的には、対象のactionsやgettersの呼出元が1箇所しかないなら、Vueコンポーネントにそれらのコードを移動します。
呼出元が複数のケースでは、Composables内でstoreに参照し、ロジックやcomputedを提供すれば解決できます。
Nuxt2でPinia導入
ここまでで、storeのリファクタは完了しているので、導入はとても楽になります。
やることは以下の2つです。
- install・setup
- VuexからPiniaへの書き換え
install・setup
ドキュメントに従い、Nuxt2でPiniaのinstallとsetupを行います。
Piniaのinstall
yarn add pinia @pinia/nuxt@0.2.1
nuxt.config.tsの更新
export default {
// ... other options
buildModules: [
'@pinia/nuxt',
],
}
tsconfig.jsonの更新
{
"types": [
// ...
"@pinia/nuxt"
]
}
VuexからPiniaへの書き換え
詳細はマイグレーションガイドに譲りますが、作業は以下の4つでした。
① storesフォルダを作成して、そこにstoreファイルを配置する
Restructuring Modules to Storesに詳細がありますが、Vuexとの設計の違いや、移行を段階的に行えるようにするため、フォルダ名がVuexと異なるようです。
そのため、新規にstoresフォルダを作成して、そこにPiniaのstoreファイルを置きます。
② Composition APIでstoreファイルを実装
Piniaは「Options API・Composition API」のどちらでも実装可能です。
私のチームでは、Piniaに限らずComposition APIで実装していく方針でしたので、Composition APIで書くことにしました。
マイグレーションガイドではOptions APIへの移行例しかありませんが、Composition APIでの書き方はCore Conceptsで確認できます。
③ storeの呼出元の書き換え
VuexとPiniaではstoreの参照方法が異なります。
修正箇所はそれなりになりますが、単純作業ですので、そこまで大変ではなかったです。
私のチームでは、modeはuniversalと、ページ遷移はSSRでしたので、コンポーネント外での利用時は、Piniaインスタンス($pinia)が必要と、そこだけ注意が必要でした。
Piniaの参照方法はUsing the storeで確認できます。
④ Testの書き換え
私のチームでは、Vitest(移行当時はJest)とTesting Libraryでテストを書いています。
テスト対象のファイルがstoreに依存している場合、テストファイルでPiniaインスタンスを作成する必要があります。
// stores/counter.spec.ts
import { setActivePinia, createPinia } from 'pinia'
import { useCounterStore } from '../src/stores/counter'
describe('Counter Store', () => {
beforeEach(() => {
setActivePinia(createPinia()) // この宣言のみでOK
})
// ...
})
テスト実行前にstateを変更したい場合は以下のように書きます。
Vuexでmockを書くのはとても面倒でしたが、Piniaではとても簡単でした。
// stores/counter.spec.ts
import { setActivePinia, createPinia } from 'pinia'
import { useCounterStore } from '../src/stores/counter'
describe('Counter Store', () => {
beforeEach(() => {
setActivePinia(createPinia())
})
it('sample', () => {
const counter = useCounterStore()
expect(counter.n).toBe(0)
counter.n = 1 // これでstateが更新されます
expect(counter.n).toBe(1) // これはpassになります
})
})
Nuxt3への移行
Piniaの移行作業は以下の2つと、ほぼ作業がなかったです。
- installのバージョンとconfigの変更
- middleware・pluginでのstore宣言の修正
installのバージョンとconfigの変更
ドキュメントに従い、Nuxt3では@pinia/nuxtの最新バージョンでinstallします。
yarn remove @pinia/nuxt && yarn add @pinia/nuxt
または
yarn upgrade --latest @pinia/nuxt
tsconfig.jsonですが、Nuxt3では以下を削除できます。
{
"types": [
// ...
- "@pinia/nuxt"
]
}
middleware・pluginでのstore宣言の修正
store宣言時にPiniaインスタンス($pinia)を渡す必要がなくなるので削除します。
Nuxt3移行時の作業はこれだけでした!
最後に
最後まで読んでいただき、ありがとうございました!
技術ブログというものを初めて書いてみましたが、記事を書くために当時の作業を振り返ったり、ドキュメントを再確認したりと、大変勉強になりました🙇♂️
Nuxt3移行のネタは大量にありまして、私の拙い文章でも誰かの役に立つかもしれないので、他のネタも記事にしようと思います🙏
この記事も、誰かの役に立てれば幸いです!
参考文献
Discussion