【Vuex】モジュール分割してストアを管理する方法【Typescript】

# 始めに
本記事は[こちらの記事](https://qiita.com/tak8_al/items/17b6cdde012cb6a8658d)の内容を発展させたものです。
良ければご覧ください。
見なくても理解には問題ないです。

# 開発環境
MacBook Air (M1, 2020)
npm 8.18.0


# 本記事の内容
* 前提
* モジュール分割
* vueファイルへの変更の反映

# 前提
以下の単純なアプリケーションをVuexでモジュール分割して、管理します。
あるのは、配列への要素の追加と、draggableのメソッドのみです。
```vuejs:TodoListView.vue(モジュール化前)
<script setup lang="ts"> 
    import { computed, reactive } from 'vue';
    import { useStore } from '../store'
    import draggable from 'vuedraggable';
    
    type TodoList = {
        id?: number;
        description: string;
    };

    const list = reactive({
        description:''
    })

    const clearForm = () => {
        list.description = ""
    };

    const store = useStore()

    const addList = () =>{
        store.dispatch('addList', {
            id: Math.floor(Math.random() * 100000),
            description: list.description
        });
        clearForm();
    }

    const todoLists = computed<TodoList[]>({
        get: () => store.getters['todoLists']
        set: val=> {
            store.dispatch('dragList', val)
        }
    })
</script>

<template>
    <div>
        <h2>タスク管理</h2>
        <form @submit.prevent="addList">
            <input type="text" id="description" v-model="list.description" /><br>
            <input type="submit" value="submit"/>
        </form>
        <hr />
        <draggable v-model="todoLists" group="list" item-key="id" class="list-index" >
            <template #item="{element}">
                <div class="list">
                    <p>{{element.description}}</p>
                </div>
            </template>
        </draggable>
    </div>
</template>
```
# モジュール分割
モジュールで分割をするためには、二つのことが必要です。
モジュール化したファイルとモジュール化したファイルの呼び込み先です。
その2つのファイルを作成していきます。

以下手順を記載します。
①ストアファイルをモジュール化
②モジュール化したファイルの呼び込み

順に解説していきます。

## ①モジュール化
モジュール化したファイルの変更点としては、一つだけです。
* Module,
* ActionTree, 
* MutationTree, 
* GetterTree
 
上記4つのオブジェクトの作成です。

```typescript: src/store/todo-list/index.ts
import { Module, ActionTree, MutationTree, GetterTree } from "vuex";

//型定義
type RootState={
    version: string
}

type TodoListsState = {
    todoLists: TodoList[];
};

type TodoList = {
    id?: number;
    description: string;
};

//ローカルストレージから情報を取得
const savedLists = localStorage.getItem('todo-lists')

export const state: TodoListsState = {
    todoLists: savedLists ? JSON.parse(savedLists): []
};

//GetterTreeのオブジェクトを用意
const getters: GetterTree<TodoListsState, RootState> = {
    getTodoLists: state => state.todoLists,
    getTodoListById: state => (list_id: number) =>state.todoLists.find(list=>list.id === list_id)!
};

//MutationTreeのオブジェクトを用意
const mutations: MutationTree<TodoListsState> = {
    addList(state, payload: TodoList){
        state.todoLists.push(payload)
    },
    dragList(state, payload: TodoList[]) {
        state.todoLists = payload
    },
};

//ActionTreeのオブジェクトを用意
const actions: ActionTree<TodoListsState, RootState> = {
    addList({commit}, payload: TodoList) {
        commit('addList', payload)
    },
    dragList({commit}, payload: TodoList[]) {
        commit('dragList', payload)
    },
}

export const TodoListsModule: Module<TodoListsState, RootState> = {
    namespaced: true,
    state,
    getters,
    actions,
    mutations,
};
```
## ②作成したモジュールの呼び込み
```typescript: src/store/todoList
import { InjectionKey } from 'vue';
import { createStore, Store, useStore as baseUseStore } from "vuex";
import { TodoListsModule } from '../store/TodoList'

//型定義
type RootState={
    version: string
}

export const store = createStore<RootState>({
  state: {
    version: '1.0.0' //超単純なプロパティを設定。
  },
  modules: {
    //importをしたモジュールを記載
    todoLists: TodoListsModule
  },
});

export const key: InjectionKey<Store<RootState>> = Symbol();

export const useStore = () => {
    return baseUseStore(key);
}

//ローカルストレージ。モジュール化したstateを呼び込みたいときは、"state.モジュール名.配列名"となる。
store.subscribe((mutation, state: any) => {
  localStorage.setItem('todo-lists', JSON.stringify(state.todoLists.todoLists))
```
# Vueファイルへの反映
モジュール名を設定したら、以下のようにコードを変更してください。
```vuejs:TodoListView.vue(変更後)
<script setup lang="ts"> 
    import { computed, reactive } from 'vue';
    import { useStore } from '../store'
    import draggable from 'vuedraggable';
    
    type TodoList = {
        id?: number;
        description: string;
    };

    const list = reactive({
        description:''
    })

    const clearForm = () => {
        list.description = ""
    };

    const store = useStore()

    const addList = () =>{
        //モジュール化した場合、呼び込みたいものの前にモジュール名/をつける。
        store.dispatch('todoLists/addList', {
            id: Math.floor(Math.random() * 100000),
            description: list.description
        });
        clearForm();
    }

    const todoLists = computed<TodoList[]>({
        //こちらも同様
        get: () => store.getters['todoLists/getTodoLists']
        set: val=> {
            //こちらも同様
            store.dispatch('todoLists/dragList', val)
        }
    })
</script>

<template>
    <div>
        <h2>タスク管理</h2>
        <form @submit.prevent="addList">
            <input type="text" id="description" v-model="list.description" /><br>
            <input type="submit" value="submit"/>
        </form>
        <hr />
        <draggable v-model="todoLists" group="list" item-key="id" class="list-index" >
            <template #item="{element}">
                <div class="list">
                    <p>{{element.description}}</p>
                </div>
            </template>
        </draggable>
    </div>
</template>
```