vuex4
的commit
,dispatch
似乎不支持直接语法提示
故在项目的store
基于vuex
的modules
的情况下,对项目进行了改造
创建项目
使用vue-cli
创建项目,勾选上ts
和vuex
修改下,使用modules
我们修改下文件,初始的vuex的结构大致是这样的
// store/index.ts
import { createStore } from 'vuex'
import modules from './modules'
export default createStore({
modules: modules,
})
// main.ts 中
import store from './store'
createApp(App).use(store)
// store/modules/index.ts
import user from "./user";
const modules = {
user,
}
export default modules
// store/modules/user.ts
import { ActionContext } from "vuex";
export interface State { // 定义该模块的state类型
name: string;
token: string;
}
export const _state = {
name: "",
token: "",
};
export const mutations = {
setName(state: State, params: string) {
state.name = params;
},
setToken(state: State, params: string) {
state.token = params;
},
};
export const actions = {
login({ commit }: ActionContext<State, any>) { // 目前没导出RootState,暂时先用any代替
commit("setToken", "token");
},
};
export default {
namespaced: true,
state: _state,
mutations,
actions,
};
定义类型
准备工作已经做的差不多,接下来我们先在store/modules/index.ts
文件下定义RootState
,用于提供给其他地方使用
// 在 store/modules/index.ts 中添加如下代码
import user, { State as UserState} from "./user";
import other, { State as OtherState} from "./other"; // 如果有其他modules,则这么写
export interface RootState {
user: UserState,
other: OtherState // 如果有其他modules,则这么写
}
给store.state
类型推断
我们取到modules返回到RootState,来作为state的类型,定义key值来使项目使用这个store
// store/index.ts
import { createStore, Store, useStore as baseUseStore } from "vuex";
import modules, { RootState } from './modules'
import { InjectionKey } from 'vue'
export default createStore<RootState>({ // 此处是为了让store在代码中直接引用取值时加的推断
modules: modules,
});
// 定义key,用于在main.ts 中使用
export const key: InjectionKey<Store<RootState>> = Symbol()
export function useStore() {
return baseUseStore(key)
}
// main.ts
import store, {key} from "./store";
createApp(App).use(store, key).use(router).mount("#app");
为了在路由文件等地方中获取vuex数据,使用createStore<RootState>
来添加推断
import store from "@/store"; console.log(store.state.user.name)
此时我们就能愉快的在vue文件中引用导出的useStore
,使用state的语法推断了。
为什么要使用导出的useStore
而不直接引用vuex
的,这是为了少点代码,否则你可能需要这么写
import { useStore } from 'vuex'
import { key } from '@/store'
setup() {
const store = useStore(key)
console.log(store.state.user.name)
}
推断modules里面的 mutations
和 actions
的值
store下新建types.ts
此处代码借鉴了ssh
大佬的文章 陪尤雨溪一起,实现 Vuex 无限层级类型推断。
改造(瞎改)了下
import { Store as BaseStore, CommitOptions, DispatchOptions } from 'vuex'
import modules, { RootState } from './modules'
type RootType = typeof modules // 推断modules类型
type AddPrefix<Prefix, Keys> = `${Prefix & string}/${Keys & string}`
type Split<S extends string, D extends string> = string extends S
? any[]
: S extends ''
? []
: S extends `${infer T}${D}${infer U}`
? [T, ...Split<U, D>]
: [S]
type GetMutations<Module> = Module extends { mutations: infer M } ? M : never
type GetActions<Module> = Module extends { actions: infer M } ? M : never
type GetModuleMutationKeys<Module, Key> = AddPrefix<Key, keyof GetMutations<Module>>
type GetModuleActionsKeys<Module, Key> = AddPrefix<Key, keyof GetActions<Module>>
type GetModulesMutationKeys<Modules> = {
[K in keyof Modules]: GetModuleMutationKeys<Modules[K], K>
}[keyof Modules]
type GetModulesActionsKeys<Modules> = {
[K in keyof Modules]: GetModuleActionsKeys<Modules[K], K>
}[keyof Modules]
type Commit = GetModulesMutationKeys<RootType>
type CommitFn = {
// 不知道怎么将 Split<F, '/'> 里的值与RootType进行优雅的绑定,先用笨方法直接写死
[F in Commit]: RootType[Split<F, '/'>[0]]['mutations'][Split<F, '/'>[1]]
}
type Dispatch = GetModulesActionsKeys<RootType>
type DispatchFn = {
[F in Dispatch]: RootType[Split<F, '/'>[0]]['actions'][Split<F, '/'>[1]]
}
export type Store = Omit<
BaseStore<RootState>,
// 'getters' |
'commit' | 'dispatch'
> & {
commit<K extends keyof CommitFn, P extends Parameters<CommitFn[K]>[1]>(
key: K,
payload?: P,
options?: CommitOptions
): ReturnType<CommitFn[K]>
} & {
dispatch<K extends keyof DispatchFn>(
key: K,
payload?: Parameters<DispatchFn[K]>[1],
options?: DispatchOptions
): ReturnType<DispatchFn[K]>
// } & {
// getters: {
// [K in keyof Getters]: ReturnType<Getters[K]>
// }
}
改造store/index.ts 让vuex的类型强制为自己修改的类型
import { createStore, Store, useStore as baseUseStore } from "vuex";
import modules, { RootState } from './modules'
import { InjectionKey } from 'vue'
import { Store as Store1 } from './types'
export default createStore<RootState>({
modules: modules,
}); as Store1
// 定义key,用于在main.ts 中使用
export const key: InjectionKey<Store<RootState>> = Symbol()
export function useStore() {
return baseUseStore(key) as Store1
}
虽然代码丑了点,但最终还是实现了目标,短时间可以凑合凑合用着
代码还有很大瑕疵,待我ts大法学成后~😄