美文网首页
ts改造vuex全链路指南

ts改造vuex全链路指南

作者: Eason_0cce | 来源:发表于2022-01-12 22:04 被阅读0次

ts发展到今天,目前主流的前端技术栈都在向其靠齐。20年我们用vue2.x+ts+vuex3.x+electron开发了一款即时聊天客户端,那时候感受到了ts强大的魅力。然而因为当时项目赶工期,对于整个项目的vuex板块并没有做很好的实践优化。对于我这样不断追求最佳实践的开发者来说是个遗憾。时隔一年,我终于可以着手将其改造,让我的团队享受我带给他们的最佳开发实践成果,同时我愿意把改造思路全盘分享给大家。

目标:实现state\module state\getter\module getter\mutation\module mutation\action\module action智能提示和类型约束。

一、实现state提示:

文件创建目录


image.png

type.ts中创建代码

export interface RootStateType {
  name: string
}

index.ts中创建代码

import { Store } from 'vuex'
import { RootStateType } from './type'

const store = new Store<RootStateType>({
  state: {},
})

此刻,你应该会得到一个错误


image.png

index.ts中输入name属性

import { Store } from 'vuex'
import { RootStateType } from './type'

const store = new Store<RootStateType>({
  state: {
    name : 'test'
  },
})

此时引入store到入口文件,得到的store类型如下


image.png

直接调用store的state属性,出现提示


image.png
在vuex目录中新建模块文件夹router
image.png

同样的方式约束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)报错


image.png

继续改造vuex(index.ts)类型约束,报错消失

import { RouterStateType } from './router/type'

export interface RootStateType {
  name: string
  router?: RouterStateType
}

store使用


image.png

截止如此,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类型


image.png

追踪定义npm中的vuex定义


image.png
改造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


image.png

补充完全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调用


image.png

好像不太对,继续改造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]>
  }
}

继续调用


image.png

到目前为止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调用


image.png
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、改变调用


image.png
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调用:


image.png
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)

好像不对


image.png

申明稍作改动

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)
image.png

截止到这里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是利器,多写多看多练才能得心应手。

相关文章

网友评论

      本文标题:ts改造vuex全链路指南

      本文链接:https://www.haomeiwen.com/subject/sofgcrtx.html