jtwang7 / Vue-Note

Vue 学习笔记
0 stars 0 forks source link

Vuex Typescript 支持:State 及 Module State 类型定义 #11

Open jtwang7 opened 2 years ago

jtwang7 commented 2 years ago

Vuex Typescript 支持:State 及 Module State 类型定义

参考文章:

顶层 State 类型定义

✅ 第一步:定义 state 类型并导出

export interface RootState {
  // ...
}

✅ 第二步:配置顶层 store 并导出

import {RootState} from 'xxx';
const store = createStore<RootState>({
  // ...
})

在调用 createStore 的时候根据将 root state 的类型定义传入 createStore 的泛型

createStore 的泛型会对应映射到顶层 root state 的类型上

✅ 第三步:注册 store 对应的唯一 key

import {InjectionKey} from 'vue'
export const key: InjectionKey<Store<RootState>> = Symbol();

InjectionKey 具体定义和作用参考 vue 官网关于 provide/inject 的使用,vuex 借助了 provide/inject 在组件中传递 store 的状态信息

✅ 第四步:在全局注册 vuex store 插件

import { createApp } from 'vue'
import App from './App.vue'
import store, { key } from '@/store'

const app = createApp(App)
app.use(ElementPlus).use(store, key).mount('#app')

🔆 在页面中使用,推荐使用 Composition API,在 vue 组件 setup 中使用

import { useStore } from 'vuex';

const store = useStore(key); // 注入 key 即可实现调用

当然有时候会觉得很麻烦, 每次都要引入key,可以对 useStore 做一层包装,统一导出,如下:

import {useStore as baseUseStore} from 'vuex';
import {key} from 'xxx';

export function useStore() {
  return baseUseStore(key)
}

子模块 State 类型定义

✅ 第一步:定义 state 类型并导出

export interface HomeModuleState {
  // ...
}

✅ 第二步:配置子模块 module 并导出

const HOME_MODULE: Module<HomeModuleState, RootState> = {
  state: () => ({
    data: [],
  }),
  getters: {},
  mutations: {},
  actions: {},
};

export default HOME_MODULE;

vuex 提供的 Module 类型接收当前模块的 module state 类型和顶层 root state 类型

✅ 第三步:将子模块注入顶层 store 中 首先在顶层 store 中增加一个全量的 state 集成 AllState,它将 store 所有的 state 类型定义按照嵌套结构映射保存。

做法:extends 继承顶层的 RootState 类型,将子模块的类型定义引入到对应的嵌套结构中,【键 homeModule 与 store 中 modules 对应的键定义同名】 新增类型定义:后续如果新增一个模块均在此全量的 AllStateTypes 新增便可

export interface AllState extends RootState {
  homeModule: HomeModuleState,
}

✅ 第四步:配置 useStore 泛型 在 modules 中引入子模块,同时我们要更新 useStore 配置: 在 baseUseStore 中我们需要手动传入泛型,baseUseStore<AllStateTypes>(key)。这样项目中就能使用所有的状态数据不会提示报错也不要在每个地方定义了。 当然,我们保留泛型的传入,将默认的类型定义为 AllStateTypes 也是可以的,如下:

export interface RootState {
  // ...
}

// store 需要注入的泛型对应顶层 state 类型,而不是整体 store state 类型
// 这是因为 createStore<T> 的泛型类型只会映射到顶层的 state
export default createStore<RootState>({
  state: () => ({...})
  modules: {
    homeModule,
  },
});

// key 传入的泛型对应 store 的类型,所以传入的也是 RootState 而不是 AllState
export const key: InjectionKey<Store<RootState>> = Symbol();

// 整体 store state 类型的嵌套映射
export interface AllState extends RootState {
  homeModule: HomeModuleState,
}

// useStore 传入的泛型对应的整个 store state 的嵌套映射
export function useStore<T = AllState>() {
  return baseUseStore<T>(key);
}

✅ 上述操作定义了 store 内部 state 的类型,此时调用 useStore() 只会提示与 state 有关的类型定义。若想要得到 getters 等其他属性的类型定义,则需要在各模块内部利用 GetterTree<T> ActionTree<T> 等显式定义类型,并赋给 getters: GetterTree<T>

jtwang7 commented 2 years ago

❇️ Vuex 项目结构组织

✅ 建议将 state / getters / ... 等分离,每个子模块各自维护,方便管理和类型定义及引用。