Vue 3、Vuex 4 模块、Typescript
在本文中,我们将了解如何使用typescript
以及Vue 3
和Vuex 4
。Vuex Modules
注意:Vuex 4 仍处于 Beta 版本。此外,随着全局变量
this
从 Vue 中移除,this.$store
Vuex 4 中也移除了 store 的 type 属性,开发者需要自行编写。更多信息请参阅vuex-release-note
我们将创建像 root 和 counter 这样的模块,其中 counter 将是模块,root 将是 RootState。让我们在 store 文件夹内创建一个模块文件夹和一个将导出我们商店的 index.ts。
我们还将拥有包含我们商店的所有接口的interfaces.ts文件。
import { ActionContext } from "vuex";
import { MutationTypes as CounterMTypes } from "./modules/counter/mutation-types";
import { ActionTypes as CounterATypes } from "./modules/counter/action-types";
export interface IRootState {
root: boolean;
version: string;
}
export interface CounterStateTypes {
counter?: number;
rootDispatch?: boolean
}
export interface CounterGettersTypes {
doubledCounter(state: CounterStateTypes): number;
counterValue(state: CounterStateTypes): number;
}
export type CounterMutationsTypes<S = CounterStateTypes> = {
[CounterMTypes.SET_COUNTER](state: S, payload: number): void;
[CounterMTypes.RESET_COUNTER](state: S): void;
};
export type AugmentedActionContext = {
commit<K extends keyof CounterMutationsTypes>(
key: K,
payload: Parameters<CounterMutationsTypes[K]>[1]
): ReturnType<CounterMutationsTypes[K]>;
} & Omit<ActionContext<CounterStateTypes, IRootState>, "commit">;
export interface CounterActionsTypes {
[CounterATypes.GET_COUNTER](
{ commit }: AugmentedActionContext,
payload: number
): void;
}
因此 CounterActionsTypes 充当计数器模块中操作的接口,CounterMutationsTypes 充当计数器模块中突变的接口,CounterGettersTypes 充当 getters 的接口。您可以从typescript-utility阅读有关 Omit 等实用程序的更多信息
让我们从创建计数器模块开始。
// store/modules/counter/state.ts
import { CounterStateTypes } from "./../../interfaces";
export const state: CounterStateTypes = {
counter: 0,
};
// store/modules/counter/action-types.ts
export enum ActionTypes {
GET_COUNTER = "GET_COUNTER"
}
// store/modules/counter/mutation-types.ts
export enum MutationTypes {
SET_COUNTER = "SET_COUNTER",
RESET_COUNTER = "RESET_COUNTER"
}
// store/modules/counter/getters.ts
import { GetterTree } from "vuex";
import {
CounterGettersTypes,
CounterStateTypes,
IRootState
} from "./../../interfaces";
export const getters: GetterTree<CounterStateTypes, IRootState> &
CounterGettersTypes = {
counterValue: (state: CounterStateTypes) => {
return state.counter || 0;
},
doubledCounter: (state: CounterStateTypes) => {
return state.counter || 0 * 2;
}
};
// store/modules/counter/mutations.ts
import { MutationTree } from "vuex";
import { MutationTypes } from "./mutation-types";
import { CounterMutationsTypes, CounterStateTypes } from "./../../interfaces";
export const mutations: MutationTree<CounterStateTypes> &
CounterMutationsTypes = {
[MutationTypes.SET_COUNTER](state: CounterStateTypes, payload: number) {
state.counter = payload;
},
[MutationTypes.RESET_COUNTER](state: CounterStateTypes) {
state.counter = 0;
}
};
// store/modules/counter/actions.ts
import { ActionTree } from "vuex";
import { ActionTypes } from "./action-types";
import { MutationTypes } from "./mutation-types";
import {
CounterActionsTypes,
CounterStateTypes,
IRootState
} from "@/store/interfaces";
export const actions: ActionTree<CounterStateTypes, IRootState> &
CounterActionsTypes = {
[ActionTypes.GET_COUNTER]({ commit }, payload: number) {
commit(MutationTypes.SET_COUNTER, payload);
}
};
// store/modules/counter/index.ts
import { Module } from "vuex";
import { CounterStateTypes, IRootState } from "@/store/interfaces";
import { getters } from "./getters";
import { actions } from "./actions";
import { mutations } from "./mutations";
import { state } from "./state";
// Module
const counter: Module<CounterStateTypes, IRootState> = {
state,
getters,
mutations,
actions
};
export default counter;
现在我们已经创建了计数器模块,我们必须添加类型,让我们在计数器模块中创建 types.ts
// store/modules/counter/types.ts
import {
CounterStateTypes,
CounterMutationsTypes,
CounterGettersTypes,
CounterActionsTypes
} from "@/store/interfaces";
import { Store as VuexStore, CommitOptions, DispatchOptions } from "vuex";
export type CounterStoreModuleTypes<S = CounterStateTypes> = Omit<
VuexStore<S>,
"commit" | "getters" | "dispatch"
> & {
commit<
K extends keyof CounterMutationsTypes,
P extends Parameters<CounterMutationsTypes[K]>[1]
>(
key: K,
payload?: P,
options?: CommitOptions
): ReturnType<CounterMutationsTypes[K]>;
} & {
getters: {
[K in keyof CounterGettersTypes]: ReturnType<CounterGettersTypes[K]>;
};
} & {
dispatch<K extends keyof CounterActionsTypes>(
key: K,
payload?: Parameters<CounterActionsTypes[K]>[1],
options?: DispatchOptions
): ReturnType<CounterActionsTypes[K]>;
};
因此,这将创建我们的计数器模块及其类型。现在让我们关注根,我们将在模块内创建一个根文件夹,它将用作 vuex 存储的根。
这就是我们的文件夹结构之后的样子。
我们在根模块中唯一需要做的额外事情就是向其中添加模块,其余的都类似于计数器模块
// store/modules/root/index.ts
import { Module, ModuleTree } from "vuex";
import { IRootState } from "@/store/interfaces";
import { getters } from "./getters";
import { actions } from "./actions";
import { mutations } from "./mutations";
import { state } from "./state";
import counterModule from "../counter";
// Modules
const modules: ModuleTree<IRootState> = {
counterModule,
};
const root: Module<IRootState, IRootState> = {
state,
getters,
mutations,
actions,
modules
};
export default root;
您可以看到我们已经将模块添加到根目录,并且可以将其传递给 createStore。
完成后,我们可以在 store 文件夹中设置 index.ts
import { createStore } from "vuex";
import { IRootState } from "@/store/interfaces";
import { CounterStoreModuleTypes } from "./modules/counter/types";
import { RootStoreModuleTypes } from "./modules/root/types";
import root from "./modules/root";
export const store = createStore<IRootState>(root);
type StoreModules = {
counter: CounterStoreModuleTypes;
root: RootStoreModuleTypes;
};
export type Store = CounterStoreModuleTypes<Pick<StoreModules, "counter">> &
Counter1StoreModuleTypes<Pick<StoreModules, "counter1">> &
RootStoreModuleTypes<Pick<StoreModules, "root">>;
createStore<IRootState>(root);
通过这种方式,我们将根模块标记为 rootState。Store
将作为我们整个商店的类型。
我还在 store 文件夹中创建了 action-types.ts 和 mutation-types.ts,以便我们可以在一个地方完成所有操作和变异。
// store/action-types.ts
import { ActionTypes as counterTypes } from "./modules/counter/action-types";
import { ActionTypes as rootATypes } from "./modules/root/action-types";
export const AllActionTypes = { ...counterTypes, ...rootATypes };
// store/mutation-types.ts
import { MutationTypes as counterTypes } from "./modules/counter/mutation-types";
import { MutationTypes as rootMTypes } from "./modules/root/mutation-types";
export const AllMutationTypes = {...counterTypes,...rootMTypes };
注意:由于我正在传播类型,如果出现相同的动作/变异类型,可能会覆盖。我们可以通过使用命名空间或完全避免这种情况来防止这种情况。
这样就完成了我们的商店,让我们看看如何在组件中使用我们的商店。
我们将在 src/use 文件夹中创建一个 useStore 实用程序。
import { Store } from "@/store";
import { useStore as VuexStore } from "vuex";
/**
* Returns Whole Store Object
*/
export function useStore(): Store {
return VuexStore() as Store;
}
现在我们可以在视图和组件中直接导入 useStore。
通过这种方式,我们可以为商店提供类型支持,并且可以在商店中添加模块和类型。
文章来源:https://dev.to/shubhadip/vue-3-vuex-4-modules-typescript-2i2o注意:我跳过了创建商店的几个步骤,本文相关的所有代码都位于Repository中。
我之前阅读过的文章,之后才能够让模块在vuex-typescript中运行。