ts发展到今天,目前主流的前端技术栈都在向其靠齐。20年我们用vue2.x+ts+vuex3.x+electron开发了一款即时聊天客户端,那时候感受到了ts强大的魅力。然而因为当时项目赶工期,对于整个项目的vuex板块并没有做很好的实践优化。对于我这样不断追求最佳实践的开发者来说是个遗憾。时隔一年,我终于可以着手将其改造,让我的团队享受我带给他们的最佳开发实践成果,同时我愿意把改造思路全盘分享给大家。
目标:实现state\module state\getter\module getter\mutation\module mutation\action\module action智能提示和类型约束。
一、实现state提示:
文件创建目录
type.ts中创建代码
export interface RootStateType {
name: string
}
index.ts中创建代码
import { Store } from 'vuex'
import { RootStateType } from './type'
const store = new Store<RootStateType>({
state: {},
})
此刻,你应该会得到一个错误
index.ts中输入name属性
import { Store } from 'vuex'
import { RootStateType } from './type'
const store = new Store<RootStateType>({
state: {
name : 'test'
},
})
此时引入store到入口文件,得到的store类型如下
直接调用store的state属性,出现提示
在vuex目录中新建模块文件夹router
同样的方式约束router(type.ts)
export interface RouterStateType {
path: string
}
实现router(router.ts)
import { Module } from 'vuex'
import { RootStateType } from '../type'
import { RouterStateType } from './type'
const router: Module<RouterStateType, RootStateType> = {
state: {
path: 'path',
},
}
export default router
改造vuex(index.ts)类型约束
import { RouterStateType } from './router/type'
export interface RootStateType {
name: string
router: RouterStateType
}
vuex(index.ts)报错
继续改造vuex(index.ts)类型约束,报错消失
import { RouterStateType } from './router/type'
export interface RootStateType {
name: string
router?: RouterStateType
}
store使用
截止如此,state及module state已经实现调用提示
二、 实现getter提示
在vuex里建getters.ts
import { GetterTree } from 'vuex'
import { RootStateType } from './type'
export interface Getters {
getName(state: RootStateType): string
}
export const getters: GetterTree<RootStateType, RootStateType> & Getters = {
getName(state) {
return state.name
},
}
在vuex的index.ts中接入getters
import { Store } from 'vuex'
import { getters } from './getters'
import { RootStateType } from './type'
const store = new Store<RootStateType>({
state: {
name: 'test',
},
getters: getters,
})
export default store
此时调用store.getters提示是any类型
追踪定义npm中的vuex定义
改造vuex中的index.ts
import { Store } from 'vuex'
import { Getters, getters } from './getters'
import { RootStateType } from './type'
const store = new Store<RootStateType>({
state: {
name: 'test',
},
getters: getters,
})
export default store as Omit<Store<RootStateType>, 'getters'> & {
getters: {
[K in keyof Getters]: ReturnType<Getters[K]>
}
}
再次调用store.getters
补充完全vuex/router/router.ts
import { Module } from 'vuex'
import { RootStateType } from '../type'
import { RouterStateType } from './type'
const routerStore: Module<RouterStateType, RootStateType> = {
namespaced : true,
state: {
path: 'path',
},
}
export default routerStore
补充vuex/index.ts
import { Store } from 'vuex'
import { Getters, getters } from './getters'
import routerStore from './router/router'
import { RootStateType } from './type'
const store = new Store<RootStateType>({
modules: {
router: routerStore,
},
state: {
name: 'test',
},
getters: getters,
})
export default store as Omit<Store<RootStateType>, 'getters'> & {
getters: {
[K in keyof Getters]: ReturnType<Getters[K]>
}
}
添加vuex/router/getters.ts
import { GetterTree } from 'vuex'
import { RootStateType } from '../type'
import { RouterStateType } from './type'
export interface RouterGetters {
getPath(state: RouterStateType): string
}
export const routerGetters: GetterTree<RouterStateType, RootStateType> &
RouterGetters = {
getPath(state) {
return state.path
},
}
改造vuex/index.ts
import { Store } from 'vuex'
import { Getters, getters } from './getters'
import { RouterGetters, routerGetters } from './router/getters'
import routerStore from './router/router'
import { RootStateType } from './type'
const store = new Store<RootStateType>({
modules: {
router: routerStore,
},
state: {
name: 'test',
},
getters: getters,
})
export default store as Omit<Store<RootStateType>, 'getters'> & {
getters: {
[K in keyof Getters]: ReturnType<Getters[K]>
} & {
[K in keyof RouterGetters]: ReturnType<RouterGetters[K]>
}
}
store调用
好像不太对,继续改造vuex/index.ts
import { Store } from 'vuex'
import { Getters, getters } from './getters'
import { RouterGetters, routerGetters } from './router/getters'
import routerStore from './router/router'
import { RootStateType } from './type'
const store = new Store<RootStateType>({
modules: {
router: routerStore,
},
state: {
name: 'test',
},
getters: getters,
})
export default store as Omit<Store<RootStateType>, 'getters'> & {
getters: {
[K in keyof Getters]: ReturnType<Getters[K]>
} & {
[K in keyof RouterGetters as `router/${K}`]: ReturnType<RouterGetters[K]>
}
}
继续调用
到目前为止getter的改造也完成了
三、改造mutation
在vuex目录中创建mutations.ts
import { MutationTree } from 'vuex'
import { RootStateType } from './type'
export interface Mutations {
SET_NAME(state: RootStateType, payload: string): void
}
export const mutations: MutationTree<RootStateType> & Mutations = {
SET_NAME(state, payload) {
state.name = payload
},
}
去vuex中的index.ts改造commit提示
import { CommitOptions, Store } from 'vuex'
import { Getters, getters } from './getters'
import { Mutations, mutations } from './mutations'
import { RouterGetters } from './router/getters'
import routerStore from './router/router'
import { RootStateType } from './type'
const store = new Store<RootStateType>({
modules: {
router: routerStore,
},
state: {
name: 'test',
},
mutations: mutations,
getters: getters,
})
export default store as Omit<Store<RootStateType>, 'getters' | 'commit'> & {
getters: {
[K in keyof Getters]: ReturnType<Getters[K]>
} & {
[K in keyof RouterGetters as `router/${K}`]: ReturnType<RouterGetters[K]>
}
commit: {
<K extends keyof Mutations>(
P: {
type: K
payload: Parameters<Mutations[K]>[1]
},
options?: CommitOptions
): void
}
}
store调用
store.commit({
type: 'SET_NAME',
payload: 'name',
})
想要无脑输入type名
1、改造mutations
import { MutationTree } from 'vuex'
import { RootStateType } from './type'
export enum MutationNames {
SET_NAME = 'SET_NAME',
}
export interface Mutations {
[MutationNames.SET_NAME](state: RootStateType, payload: string): void
}
export const mutations: MutationTree<RootStateType> & Mutations = {
[MutationNames.SET_NAME](state, payload) {
state.name = payload
},
}
2、改变调用
store.commit({
type: MutationNames.SET_NAME,
payload: 'name',
})
实现router模块下面的mutations,在vuex/router创建mutations.ts
import { MutationTree } from 'vuex'
import { RouterStateType } from './type'
export enum RouterMutationNames {
SET_PATH = 'SET_PATH',
}
export interface RouterMutations {
[RouterMutationNames.SET_PATH](state: RouterStateType, payload: string): void
}
export const routerMutations: MutationTree<RouterStateType> & RouterMutations =
{
[RouterMutationNames.SET_PATH](state, payload) {
state.path = payload
},
}
改变vuex/index.ts的store类型
import { CommitOptions, Store } from 'vuex'
import { Getters, getters } from './getters'
import { Mutations, mutations } from './mutations'
import { RouterGetters } from './router/getters'
import { RouterMutations } from './router/mutations'
import routerStore from './router/router'
import { RootStateType } from './type'
const store = new Store<RootStateType>({
modules: {
router: routerStore,
},
state: {
name: 'test',
},
mutations: mutations,
getters: getters,
})
export default store as Omit<Store<RootStateType>, 'getters' | 'commit'> & {
getters: {
[K in keyof Getters]: ReturnType<Getters[K]>
} & {
[K in keyof RouterGetters as `router/${K}`]: ReturnType<RouterGetters[K]>
}
commit: {
<K extends keyof Mutations>(
P: {
type: K
payload: Parameters<Mutations[K]>[1]
},
options?: CommitOptions
): void
<K extends keyof RouterMutations>(
P: {
type: `router/${K}`
payload: Parameters<RouterMutations[K]>[1]
},
options?: CommitOptions
): void
}
}
store调用:
store.commit({
type: `router/${RouterMutationNames.SET_PATH}`,
payload: 'path',
})
vuex/router/index.ts接入routerMutation
import { Module } from 'vuex'
import { RootStateType } from '../type'
import { routerMutations } from './mutations'
import { RouterStateType } from './type'
const routerStore: Module<RouterStateType, RootStateType> = {
namespaced: true,
state: {
path: 'path',
},
mutations: routerMutations,
}
export default routerStore
store注入vue有点尴尬,只能这么写:
new Vue({
router,
store: store as Store<any>,
render: (h) => h(App),
}).$mount('#app')
检验结果
console.log(store.state.name)
console.log(store.state.router?.path)
store.commit({
type: `router/${RouterMutationNames.SET_PATH}`,
payload: 'path',
})
console.log(store.state.router?.path)
store.commit({ type: MutationNames.SET_NAME, payload: 'new name' })
console.log(store.state.name)
好像不对
申明稍作改动
import { CommitOptions, Store } from 'vuex'
import { Getters, getters } from './getters'
import { Mutations, mutations } from './mutations'
import { RouterGetters } from './router/getters'
import { RouterMutations } from './router/mutations'
import routerStore from './router/router'
import { RootStateType } from './type'
const store = new Store<RootStateType>({
modules: {
router: routerStore,
},
state: {
name: 'test',
},
mutations: mutations,
getters: getters,
})
export default store as Omit<Store<RootStateType>, 'getters' | 'commit'> & {
getters: {
[K in keyof Getters]: ReturnType<Getters[K]>
} & {
[K in keyof RouterGetters as `router/${K}`]: ReturnType<RouterGetters[K]>
}
commit: {
<K extends keyof Mutations>(
type: K,
payload: Parameters<Mutations[K]>[1],
options?: CommitOptions
): void
<K extends keyof RouterMutations>(
type: `router/${K}`,
payload: Parameters<RouterMutations[K]>[1],
options?: CommitOptions
): void
}
}
再次检验
console.log(store.state.name)
console.log(store.state.router?.path)
store.commit(`router/${RouterMutationNames.SET_PATH}`, 'new path')
console.log(store.state.router?.path)
store.commit(MutationNames.SET_NAME, 'new name')
console.log(store.state.name)
截止到这里mutation的变种也书写完成了
四、接下来,就是你们自己自由发挥的时间了
我们公司真正使用的语法如下:
store.commit(MutationTypes.UpdateLanguageVersion, 'en');
store.moduleCommit(ModuleNames.Router, RouterMutationTypes.UpdateName, 'ease');
store.dispatch(ActionTypes.UpdateUserInfo, {
userType: true,
isAdmin: 'admin',
sysUserName: 'name',
mabangExternalId: '001',
companyId: 'c01',
language: 'en',
});
store.moduleDispatch(ModuleNames.Router, RouterActionTypes.UpdateName, 'en');
@State(RootStateNames.Router) router!: RootStateTypes[RootStateNames.Router];
@routerModule.State(RouterStateNames.Name) name!: RouterStateTypes[RouterStateNames.Name];
@Getter(GetterNames.GetCount) getCount!: GetterTypes[GetterNames.GetCount];
@routerModule.Getter(RouterGetterNames.GetName) getName!: RouterGetterTypes[RouterGetterNames.GetName];
最后表达:ts是利器,多写多看多练才能得心应手。