monorepo中使用VUEX
monorepo项目与其他独立项目不同的是,它是由许多独立的项目组合而成的一个项目,各个项目可以独立运行打包,但是他们之间可能复用了一些相同的数据。所以各个项目的状态管理就存在了这样一个情况各个项目都会用到了一些状态数据,比如token
,页面级loading
的开启关闭(需要配合拦截器)等,但是各个项目中又会管理自己的状态比如项目demo1
内存在一个name
字段,项目demo2
存在一个count
字段,这俩又是互不干扰的。所以vuex
的设计模式如下
目录结构的调整
├── config // vite相关配置
├── src
| ├── common // 通用方法
| | ├── store // 新增目录
| | | ├── index.ts // store入口
| | | ├── commonModule.ts // 存放通用数据
| | | ├── interface.ts
| ├── packages // 包文件
| | ├── demo1
| | | ├── index.html
| | | ├── main.ts
| | | ├── config.json
| | | ├── App.vue
| | | ├── store // 新增目录
| | | | ├── index.ts // demo1所独有的store
| | ├── demo2
| | | ├── index.html
| | | ├── main.ts
| | | ├── App.vue
| | | ├── store // 新增目录
| | | | ├── index.ts // demo2所独有的store
├── package.json
├── vite.config.ts
代码实现
通用store代码
// src/common/store/commonModule.ts
export interface ICommonModule {
isShowLoading: boolean
token: string
}
export default {
namespaced: true,
state() {
return {
isShowLoading: false,
token: ''
} as ICommonModule
},
mutations: {
showLoading(state: ICommonModule) {
state.isShowLoading = true
},
hideLoading(state: ICommonModule) {
state.isShowLoading = false
}
},
actions: {}
}
// src/common/store/interface.ts
import { ICommonModule } from './commonModule'
export interface IStore {
commonModule: ICommonModule
}
关键代码
// src/common/store/index.ts
import { createStore, Store, useStore as baseUseStore } from 'vuex'
import { IStore } from './interface'
import { InjectionKey } from 'vue'
import commonModule from './module/commonModule'
const modulesFiles = import.meta.globEager('../../packages/*/store/index.ts')
let modules = Object.keys(modulesFiles).reduce((modules: { [key: string]: any }, path: string) => {
const moduleName = path.split('packages/').pop().split('/')[0] + 'Module'
modules[moduleName] = modulesFiles[path]?.default
return modules
}, {})
Object.keys(modules).forEach(item => {
modules[item]['namespaced'] = true
})
export const key: InjectionKey<Store<IStore>> = Symbol()
export default createStore<IStore>({
modules: {
commonModule,
...modules
}
})
export function useStore() {
return baseUseStore(key)
}
// src/packages/demo1/store/index.ts
interface IMono1Module {
name: string
}
declare module '@store/interface' {
interface IStore {
mono1Module: IMono1Module
}
}
export default {
state() {
return {
name: '测试'
}
},
mutations: {
test(state) {
state.name+= 'A'
}
},
actions: {},
getters: {}
}
思路就是利用vite
的import.meta.globEager
来自动引入各个独立项目的store
,组装进modules
内。但是会引入一个问题,打包demo1时会把demo2内的store
也打包进来,这样就不合理了。 我这边是通过vite写了个一个自定义插件,打包demo1时会用../../packages/demo1/store/index.ts
替换 ../../packages/*/store/index.ts
这样打包后的modules
只会存在一个commonModule
和一个demo1Module
各个项目中的store
要注意的一点是,可以利用declare
直接在通用的IStore
中声明自己的类型。
在项目中使用
import { useStore } from '@store'
const store = useStore()
console.log(store.state.mono1Module.name) // 测试
store.commit('mono1Module/test')
console.log(store.state.mono1Module.name) // 测试A
store.commit('commonModule/showLoading') // 开启loading
简单的使用是没有问题的,而且代码提示,和ts校验都是可以正常生效
这个monorepo项目也是用到了axios
,axios
拦截器也是项目必不可少的,下一章讲一下这个项目如何配置拦截器的