TypeScript Vuex4
苏苏同学 人气:1简介
虽然TypeScript
知识点学了很多,但是在实际项目中很多小伙伴还并不知道怎么用,今天笔者结合Vuex4
来使用TypeScript
实战一下。
本文分vuex4
类型 Api
分析和vuex4
实战两部分讲述。
首先我们来分析下 vuex4
的类型 Api
createStore
我们可以先看看createStore
方法,发现它需要传递一个泛型,并且这个泛型会应用到state
上。
export function createStore<S>(options: StoreOptions<S>): Store<S>; export interface StoreOptions<S> { state?: S | (() => S); getters?: GetterTree<S, S>; actions?: ActionTree<S, S>; mutations?: MutationTree<S>; modules?: ModuleTree<S>; plugins?: Plugin<S>[]; strict?: boolean; devtools?: boolean; }
GetterTree
我们可以看到getters
的类型GetterTree
接收了两个泛型。
export interface GetterTree<S, R> { [key: string]: Getter<S, R>; }
这两个泛型分别应用在本模块state
上和根state
上。
export type Getter<S, R> = (state: S, getters: any, rootState: R, rootGetters: any) => any;
MutationTree
我们可以看到mutations
的类型MutationTree
只接收了一个泛型。
export interface MutationTree<S> { [key: string]: Mutation<S>; }
并且这个泛型只应用到本模块state
上。
export type Mutation<S> = (state: S, payload?: any) => any;
ActionTree
我们可以看到actions
的类型ActionTree
接收了两个泛型。
export interface ActionTree<S, R> { [key: string]: Action<S, R>; }
并且也将这两个泛型分别应用在本模块state
上和根state
上。我们还可以看到,由于action
支持对象和方法形式的写法,所以Action
的类型是ActionHandler
和ActionObject
的联合类型。
export type Action<S, R> = ActionHandler<S, R> | ActionObject<S, R>; export type ActionHandler<S, R> = (this: Store<R>, injectee: ActionContext<S, R>, payload?: any) => any; export interface ActionContext<S, R> { dispatch: Dispatch; commit: Commit; state: S; getters: any; rootState: R; rootGetters: any; }
ModuleTree
我们可以看到modules
的类型ModuleTree
只接收了一个泛型。并且将这个泛型传递到了Module
里面。
export interface ModuleTree<R> { [key: string]: Module<any, R>; }
我们可以发现,module
的类型和createStore
的StoreOptions
是非常像的。
因为模块本身也有自己的state、getters、actions、mutations、modules
export interface Module<S, R> { namespaced?: boolean; state?: S | (() => S); getters?: GetterTree<S, R>; actions?: ActionTree<S, R>; mutations?: MutationTree<S>; modules?: ModuleTree<R>; }
了解了vuex4
类型 Api
接下来我们就进入到实战环节了。
实战
首先我们需要使用vuecli
创建一个TypeScript
的项目。
整体目录结构
笔者store整体目录结构如下:
index.ts
和以前的还是一样,用来创建根store
并导出根store
。interfaces.ts
用来存放store
的类型。modules.ts
和以前的还是一样,用来存放模块。
首先定义根state的类型
// interfaces.ts type Info = { address: string }; export interface IRootState { name: string; age: number; info: Info; }
在创建store的时候将根state的类型传递进去。
// index.ts import { createStore, Store } from "vuex"; import { InjectionKey } from "vue"; import { IRootState } from "./interfaces"; export default createStore<IRootState>({ state: { name: "root", age: 0, info: { address: "" }, }, getters: { getRootName(state) { return state.name; }, getRootInfo(state) { return state.info; }, }, mutations: {}, actions: {}, });
并且需要导出key
// index.ts export const key: InjectionKey<Store<IRootState>> = Symbol();
在Vue实例使用store的时候将key一并传入。
// main.ts import store, { key } from "@/store"; createApp(App).use(store, key).mount("#app");
在vue组件使用
这样我们在vue
组件就能享受到TypeScript
的优势啦。
注意这里的useStore()
,也需要我们把key
传递进去。
import { useStore } from "vuex"; import { key } from "@/store"; setup() { const store = useStore(key); return { rootName: store.state.name, }; },
可以看到,我们使用state
的时候就会被自动提示啦。
并且当你使用不存在的属性时会在我们编写代码的时候就会直接报错提示。
相较js
,大大提高了开发效率不说,还减少了bug
。
自定义useStore()方法
如果觉得每次useStore()
,还需要我们把key
传递进去麻烦的话,我们可以创建自己的useStore()
方法。
// index.ts import { useStore as baseUseStore } from "vuex"; export function useStore() { return baseUseStore(key); }
这样我们在vue
组件使用store
的时候引入自己的useStore
方法就可以啦。
import { useStore } from "@/store"; setup() { const store = useStore(); return { rootName: store.state.name, }; },
我们知道,在实际项目中只创建一个根store
是远远不够的。一般我们都会使用modules
。下面笔者介绍下怎么使用modules
。
modules的使用
模块的类型是Module
,并且需要传递本模块state
类型和根state
类型。
本模块state
类型需要传递进去我们可以理解,但是为什么要传递根state
类型呢?
因为我们的getters
和actions
参数里面是有rootState
的,所以需要引入根state
类型。
// modeuls/test1.ts import { Module } from "vuex"; import { IRootState, ITest1State } from "../interfaces"; const Test1: Module<ITest1State, IRootState> = { state: { name: "test1", count: 0, }, getters: { getTest1Name(state) { return state.name; }, getAllName(state, rootState) { return state.name + rootState.age; }, }, }; export default Test1;
创建好模块后我们需要在根store
里面引入进去,引入方式和以前还是一样。
并且我们需要把模块的state
类型一并传递到InjectionKey
中和根state
类型形成交叉类型,并重新生成key
。
// index.ts import { createStore, Store, useStore as baseUseStore } from "vuex"; import { InjectionKey } from "vue"; import { IRootState, ITest1State } from "./interfaces"; import test1 from "./modeuls/test1"; export default createStore<IRootState>({ // ... modules: { test1: test1, // ...多个模块,类似 }, }); // 定义模块类型 type Modules = { test1: ITest1State; // ...多个模块,类似 }; // 使用交叉类型形成新的key export const key: InjectionKey<Store<IRootState & Modules>> = Symbol();
我们来看看在vue
组件中的使用效果。
我们可以发现,当我们使用state
的时候,test1
模块也会被提示出来
并且它里面的属性也会被直接提示出来
好了,实战环节就讲述的差不多了,小伙伴们时候都懂了呢?
总结
虽然vuex4
对TypeScript
有了很好的支持,但是笔者觉得还是不够的。
比如 使用麻烦,每次需要定义一个key
,还需要把可以传递到vue
的use
方法里面,并且在使用useStore
的时候也还需要将key
传递进去,无疑增加了开发成本。
其次,这样的配置并不会对getters、mutations、actions
生效,只会对state
有提示。
和pinia
比起来,整体使用体验还是会差很多。如果想学习pinia,并想了解pinia和vuex区别的话可以看看TypeScript Pinia实战分享(Vuex和Pinia对比梳理总结)一文。
加载全部内容