# StoreModule

# 介绍

在实际的项目开发中,状态管理必不可少,为了方便开发与统一维护,项目集成了Vuex,并实现了自动导入功能,且制定了 StoreModule规则

重点:

项目会自动导入 @/store/***.ts(全局 Store 数据模型目录下所有StoreModule文件)、@/views/***/store.ts(页面组件目录下所有文件名为store.ts文件)。

如果StoreModulename字段参数出现重名,权重如下:

@/store/***.ts > @/views/***/store.ts

# StoreModuleType

为了规范统一方便开发与维护,实现自动化导入,项目制定了 StoreModuleType 规则。

// @/utils/store.ts

import { Module } from 'vuex';

/**
 * 自定义项目 Store Module 类型
 * 为自动导入的 store 做类型限制
 * [@/store文件夹下定义的 store]与[@/views文件夹下定义的`文件store.ts`] 必须继承此类型
 * @author LiQingSong
 */
export interface StoreModuleType<S> extends Module<S, S> {
  namespaced: true;
  name: string;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

注意重点:

@/store/***.ts@/views/***/store.ts 必须继承 StoreModuleType 此类型,因为StoreModuleType定义了name字段参数必填,这样做的原因是自动导入需求需要name

# 使用

项目允许在 @/store目录下 创建任意文件名的.ts文件与 @/views目录下任意目录下store.ts文件会被自动导入。

├── src                        # 源代码
│   ├── store                  # 全局 Store 数据模型目录(Vuex)
│   │   ├── global.ts          # 全局 StoreModule
│   │   └── user.ts            # user 公共StoreModule
│   ├── views                  # 页面组件目录(所有页面放在这里)
│   │   └── home               # 页面-首页
│   │       ├── data.d.ts      # TS 类型定义文件(可选)
│   │       ├── index.vue      # 当前页面入口
│   │       ├── service.ts     # 当前页面数据接口文件(可选)
│   │       └── store.ts       # 当前页面数据模型文件(可选)
1
2
3
4
5
6
7
8
9
10

以上可以看出 @/store目录下的 global.tsuser.ts@/views/home目录下的 store.ts 就是 StoreModule

# 1、定义

@/store/user.ts 为例,其内容如下:

import { Mutation, Action } from 'vuex';
import { StoreModuleType } from "@/utils/store";
import { ResponseData } from '@/utils/request';
import { queryCurrent, queryMessage } from "@/services/user";
import { removeToken } from "@/utils/localToken";


export interface CurrentUser {
  id: number;
  name: string;
  avatar: string;
  roles: string[];
}

// 定义 state 类型
export interface StateType {
  currentUser: CurrentUser;
  message: number;
}

// 定义 全局user模型类型,必须继承 `StoreModuleType` 这样可以限制 `name` 参数必填
export interface ModuleType extends StoreModuleType<StateType> {
  // state 类型
  state: StateType;
  // mutations(同步) 定义
  mutations: {
    saveCurrentUser: Mutation<StateType>;
    saveMessage: Mutation<StateType>;
  };
  // actions(异步) 定义
  actions: {
    fetchCurrent: Action<StateType, StateType>;
    fetchMessage: Action<StateType, StateType>;
    logout: Action<StateType, StateType>;
  };
}

// 定义 state 初始化值
const initState: StateType = {
  currentUser: {
    id: 0,
    name: '',
    avatar: '',
    roles: [],
  },
  message: 0,
}

// 定义 UserStoreModel
const StoreModel: ModuleType = {
  namespaced: true,
  // 名称必填,且唯一
  name: 'user',
  // state 初始化值必须解构赋值
  state: {
    ...initState
  },
  mutations: {
    saveCurrentUser(state, payload) {
      state.currentUser = {
        ...initState.currentUser,
        ...payload,
      }
    },
    saveMessage(state, payload) {
      state.message = payload;
    }
  },
  actions: {
    async fetchCurrent({ commit }) {
      try {
        const response: ResponseData = await queryCurrent();
        const { data } = response;
        commit('saveCurrentUser', data || {});
        return true;
      } catch (error) {
        return false;
      }
    },
    async fetchMessage({ commit }) {
      try {
        const response: ResponseData = await queryMessage();
        const { data } = response;        
        commit('saveMessage', data || 0);
        return true;
      } catch (error) {
        return false;
      }
    },
    async logout({ commit }) {
      try {
        await removeToken();
        commit('saveCurrentUser', { ...initState.currentUser });
        return true;
      } catch (error) {
        return false;
      }
    }
  }
}


// 最终导出定义 Model
export default StoreModel;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104

# 2、使用

<template>
    <div v-if="loading">加载中</div>
    <div v-else>{{currentUser}}</div>
</template>
<script lang="ts">
import { computed, defineComponent, onMounted, ref } from "vue";
import { useStore } from "vuex";
import { StateType as UserStateType, CurrentUser } from "@/store/user";

interface DemoSetupData {
    loading: boolean;
    currentUser: CurrentUser;
}

export default defineComponent({
    name: 'Demo',
    setup(): DemoSetupData {
       
        const store = useStore<{user: UserStateType}>();

        // 获取当前登录用户信息
        const currentUser = computed<CurrentUser>(()=> store.state.user.currentUser);

        const loading = ref<boolean>(false);
        const getUser = async () => {
            loading.value = true;
            await store.dispatch('user/fetchCurrent');                  
            loading.value = false;
        }

        onMounted(() => {
            getUser();
        })

        return {
            loading: loading as unknown as boolean,
            currentUser: isLogin as unknown as CurrentUser,           
        }
    }
})
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41