【備忘録】Nuxt.js + Composition API でVue3 + Vuex4のように書く

## はじめに
会社でとある業務管理システムを開発していて、最近Vue2→Vue3に書き換えました。
しかしながら、だいぶ複雑な構造になりそうだったので、Nuxt.jsに乗せようと思い着手しました。
NuxtでもVue3 + Vuex4のまま書きたかったのですが、意外に試行錯誤したので、ここに備忘録として残します。

## 本記事で触れる事
Vue3とVuex4の基本的な記述を、Nuxt.js + Composition APIを使用して再現する
※Nuxt.jsの環境構築等は省略します。詳細は公式を参照してください。

- [Nuxt.js 公式](https://ja.nuxtjs.org/)
- [Nuxt TypeScript](https://typescript.nuxtjs.org/ja)


## 開発環境
### 移行前（Vue3）の環境
```Package.json
"dependencies": {
	"balm-ui": "^9.29.1",
	"moment": "^2.29.1",
	"theme": "^0.1.0",
	"typescript": "^4.3.2",
	"uikit": "^3.6.21",
	"vue": "^3.0.11",
	"vue-router": "^4.0.8",
	"vuex": "^4.0.0",
	"vuex-persistedstate": "^4.0.0-beta.1"
},
"devDependencies": {
	"@parcel/transformer-typescript-tsc": "^2.0.0-beta.3.1",
	"@parcel/transformer-vue": "2.0.0-beta.2",
	"@parcel/validator-typescript": "^2.0.0-beta.3.1",
	"@types/uikit": "^3.3.1",
	"parcel": "^2.0.0-beta.2",
	"sass": "^1.32.13"
},
```

### 移行後(Nuxt2)の環境
```Package.json
"dependencies": {
	"@nuxtjs/axios": "^5.13.6",
	"@nuxtjs/composition-api": "^0.24.5",
	"@nuxtjs/dotenv": "^1.4.1",
	"balm-ui": "^8.40.4",
	"core-js": "^3.15.1",
	"moment": "^2.29.1",
	"nuxt": "^2.15.7",
	"theme": "^0.1.0"
},
"devDependencies": {
	"@nuxt/types": "^2.15.7",
	"@nuxt/typescript-build": "^2.1.0",
	"@types/uikit": "^3.3.1",
	"fibers": "^5.0.0",
	"nuxt-svg-loader": "^1.2.0",
	"sass": "^1.35.1",
	"sass-loader": "10.1.1",
	"uikit": "^3.6.22"
}
```
※開発途中なので、まだ増えたり減ったりする予定です。

### プロジェクト内ディレクトリ構造
ほとんどデフォルトのままです。※のところが解説で使用するファイルです。

```
├── assets
├── components
├── layouts
├── plugin
├── types
├── utils
├── pages
│   └── index.vue　※
├── store
│   ├── module1.ts　※
│   └── module2.ts　※
├── package.json
├── tsconfig.json
├── nuxt.config.ts
└── yarn.lock
```

## @nuxtjs/composition-apiを使ってVue3 + Vuex4のように書く
Nuxtプロジェクトに`@nuxtjs/composition-api`を追加します。
ちなみにnuxtjs/composition-apiの公式は [こちら](https://ja.nuxtjs.org/) です。

### Vuex4で書いたStoreを少し書き直す
`createStore`は`@nuxtjs/composition-api`やNuxtの`vuex`にはなかったので、
試行錯誤しましたが、結局以下の書き方に落ち着きました。`module2.ts`も同じように書きました。

```store/module1.ts
import { InjectionKey, useStore as baseUseStore } from "@nuxtjs/composition-api";
import { Store as VuexStore, Commit } from "vuex";

//ファイル名（module1）でまとめる
export type StoreType1 = {
    module1: StateType1
}

type StateType1 = {
    data1: string;
    data2: string;
}

export const key: InjectionKey<VuexStore<StateType1>> = Symbol();

//CreateStoreは使えない
export const state = (): StateType1 => ({
    data1: "テスト1",
    data2: "テスト2",
});

export const mutations = {
    //省略
};

export const actions = {
    setTestAction({ commit }: { commit: Commit }, payload: string) {
        //Mutation.SET_TEST_ACTION は別ファイルにて定数で名前を定義
        commit(Mutation.SET_TEST_ACTION, payload);
    },
};

//useStoreをエクスポート
export const useStore = () => {
	return baseUseStore(key);
};
```

### storeをインポートする
`index.vue`内は以下のようにしました。

```pages/index.vue
<template>省略</template>
<script lang="ts">
    //useStoreはVue3ではvuexからインポートしていたが、composition-apiからインポートする
    import { defineComponent, reactive, useStore } from "@nuxtjs/composition-api";
    //store内stateのtypeをインポート
    import { StoreType1 } from "~/store/module1.ts"
    import { StoreType2 } from "~/store/module2.ts"
    
    //Vue3のcomposition APIと変わらない
    export default defineComponent({
        setup() {
            const data = reactive({
                aaa: "",
                bbb: "",
            });
            //storeをインスタンス化
            const store = useStore<StoreType1 & StoreType2>();
            //console.log(store) => storeのモジュールがまとまって取得されている

            //stateを試しに参照 - gettersも普通に使えると思われる
            const m1 = store.state.module1;
            const m2 = store.state.module2;
            console.log(m1.data1) //=> テスト1
            console.log(m1.data2) //=> テスト2

            //値をセット
            const setDatas = async () => {
                //[モジュール名(ファイル名)]/[actionsで設定した名前]
                await store.dispatch("module1/setTestAction", "test")
            }
            
            //以下省略

            return { data };
        },
    });
</script>
<style lang="scss" scoped>
    //省略
</style>
```

## 最後に
せっかくVue3の書き方に慣れたところで、そのままの記述で移行できないかと試行錯誤しました。
いろいろ調べたのですが、情報を探すのになかなか苦労したので記事にしました。
まだまだ改善すべき点はあるとは思いますが、まずは記録として。

## その他参考記事
NuxtをTypeScript化する際に参考にさせて頂きました。

https://qiita.com/miiiii/items/7d79f4f02303e8e486e3