[Vue]VuexからPiniaへ移行

# はじめに

* プロジェクトでVuexをPiniaへ移行する作業を行ったのでその作業手順メモ。

* store数が多かったので、VuexとPiniaを並行導入しつつ、段階的にPiniaへ移行した。

* 基本的にはマイグレーションガイドを見れば問題なかった。

https://pinia.vuejs.org/cookbook/migration-vuex.html

# 移行手順の記録

## piniaのインストール

```
yarn add pinia
```

## main.tsの設定

* `createPinia()`を追加する。並行稼働するため、Vuexのstore設定も残す
* VuexはDynamicImportをするために、store/index.tsで空storeを作成してる
* PiniaではDynamicImportの設定は不要（必要な時に作られる）なので、storeの初期化不要。

```diff_javascript:main.ts
import store from '@/store' // Vuexのstore初期化ファイル。
+ import { createPinia } from 'pinia'
...
app.use(store)
+ app.use(createPinia())
...
```

## storesフォルダにstoreファイルを作成

* Vuexはstoreフォルダ(sなし)で別で管理できるので、並行稼動ができる。
* setup構文が使えるので、stateはref関数、getterはcomputed、actionは関数で定義できる。
    * ※なお、mutationは存在せず、actionに含める。
* 名称はuseXXXとし、composition-apiとして配布する。
* 以下、before, after例。なお、beforeは`vuex-module-decorators`記法で記載。通常のVuex記法はマイグレーションガイドに記載されているので、そちらを参照。
    * ※vuex-module-decorators：クラス構文と専用のデコレータでvuexの定義が行える。クラスの恩恵が受けれるため、型定義などがしやすい。が、関数型プログラミンが主流となったことで利用されなくなった。（現在メンテされていない）

```ts:before（vuex-module-decorators記法）
import { VuexModule, Module, Action, Mutation, getModule } from 'vuex-module-decorators'
import store from '@/store'

export interface ICompany {
  id: string
  name: string
}

// DynamicImportの設定
@Module({ dynamic: true, store, name: 'company' })
class Company extends VuexModule implements ICompany {
  // stateはフィールド
  public id = ''
  public name = ''
  
  // getter
  get comanyIdName() {
    return `${this.id}:${this.name}`
  }

  @Mutation
  private SET_COMPANY(company: ICompany) {
    this.id = company.id
    this.name = company.name
  }

 // アノテーションで設定する
  @Action
  public setCompany(company: ICompany) {
    this.SET_COMPANY(company)
  }
}

export const CompanyModule = getModule(Company)

```

```ts:after(Pinia-setup記法)

import { defineStore } from 'pinia'
import { computed, ref } from 'vue'

export interface ICompany {
  id: string
  name: string
}

export const useCompanyStore = defineStore('company', () => {
  // stateはref変数
  const id = ref('')
  const name = ref('')

  // gettterはcomputed
  const companyIdName =computed(()=> `${id.value}:${name.value}`)

  // actionはmutationと統合して、関数
  const setCompany = (company: ICompany) => {
    id.value = company.id
    name.value = company.name
  }

  return { id, name, companyIdName, setCompany }
})

```

* ちなみに、returnするものが多い場合は、定義時に関数化してまとめるとよい。

```javascript
export const useUserStore = defineStore('XXX', () => {

  const useUserState = () => {
    return {
      name: ref(''),
      email: ref(''),
      ...
    }
  }
  const userState = useUserState()

  const useComputables = () => {
    return {
      isUser: computed(() => XXXX),
      isCompany: computed(() => XXXX),
      ...
    }
  }
  const computables = useComputables()

  return { ...userState, ...computables }
```

## 利用側の修正

* 作成したuse関数を使用し、利用箇所を置き換えるだけ。

```diff_javascript
- import { CompanyModule } from '@/store/modules/company'
+ import { useCompanyStore } from '@/stores/company'

// use関数を使用する。（変数名も同じ（CompanyModule）にすれば実質変更はここだけ。）
+ const companyStore = useCompanyStore()

...
-  CompanyModule.setCompany({
+  companyStore.setCompany({
    name: item.name,
    id: item.id
  })
- const id = companyModule.id
+ const id = companyStore.id

```

* tsファイルの場合、topレベルで呼び出すと`undefinde`になってしまうので、関数内で取得する
    * ここで、はまってしまった。がよく考えれば、`script setup` も`defineComponent`関数のシンタックスシュガーだったから、関数内で取得しているのか。
    * 公式の移行手順に思いっきり書かれてた。。。

https://pinia.vuejs.org/cookbook/migration-vuex.html#Usage-Outside-Components


## Vuexの削除

* すべての動作確認が終わり、移行が問題なければ、Vuexの資材を削除する。
    * `store`フォルダの削除
    * main.tsの設定削除

```diff_javascript:main.ts
- import store from '@/store' // Vuexのstore初期化ファイル。
...
- app.use(store)
...
```

* 最後に、Vuexをアインストールして作業完了
    * package.jsonフォルダ直下で以下を実行

```
yarn remove vuex
yarn remove vuex-module-decorators
```

* 念のためキャッシュクリア、node_module入れ替えを行い（以下）、動作を再度確認した。

```
rm -rf node_modules
yarn cache clean 
yarn install
```

# おわりに

* 移行は、他のVu3系ライブラリに比べて格段に簡単だった。
* mutationがなくなったこと、setup構文で記載できることで、非常に簡潔にかけるようになった。
* Vuexで問題視されていた、複雑な型定義も解消されたので、Pinia移行は非常におすすめ。
    * ※複雑な型定義問題で、`vuex-module-decorators`を使用する経緯となっった。