vuex-cuer v0.2.4
vuex-cuer
简介
还在为 vuex 的魔法字符串而烦恼?还在为阅读项目里 vuex 相关的代码而头痛?
如何一目了然的知道某个 type 的功能?定义字符串常量加以注释 或者 使用d.ts文件进行注释?
那如何快捷的定位到 commit 和 dispatch 相应的type 的原函数?
你可以来试试 vuex-cuer ,纵享丝滑
地址
示例
将项目 clone 下来之后执行 npm run dev 即可启动 demo ,当然前提是你已经初始化了。
效果
推荐直接通过
commits调用函数,因为这样有能查看到原函数的注释
兼容
commit函数调用,并且优化了提示
诞生
由来
这个库的诞生是因为我实在受不了接手的这个项目里的
vuex相关的代码,阅读起来令我头皮发麻 于是我决定使用Typescript基于vuex来封装一个有类型约束的库。
结构
但是在设计之初我遇到了
循环引用的问题,场景是:定义一个store对象的时候参数有mutations等等,然后需要在mutations的函数中访问store对象,如以下代码:const store = new Vuex.Store({ state: { count: 1 }, mutations: { add (state) { state.count++ store.xxx } } });问题在于这个时候
store还没出生,如何在add方法中使用store呢? 最终我确定了一个思路:以Store的类为核心,通过泛型关联其他四个属性State、Mutations、Actions、Getters,如下结构:
然后还是这个问题,如何在 `add` 方法中使用 `store`?既然把 `Mutations` 封装成了类,可以把 `store` 这个对象绑定到 `Mutations` 的实例的 `this` 上面,于是就有了这个约束:
```typescript
abstract class ICuer<T> {
protected readonly store!: T;
}
```
这便是 `Mutations`、`Actions`、`Getters` 的基类。<br>
但是这样还不够,`state` 对象也应该关联上,正好一起绑上去,这样同一个 `Mutations` 的函数互相访问可以省略第一个参数 `state`:
```typescript
interface IState<S = unknown> {
state: S;
}
abstract class ICuer<T extends IState = IState> {
protected readonly state!: T["state"];
protected readonly store!: T;
}
```
于是乎就实现了:
```typescript
class Mutations extends ICuer<ExampleStore> {
/**
* 加 `1`
*/
addOne() {
this.add(1);
}
/**
* 加 `v`
*/
add(v: number) {
this.state.value += v;
this.store.xxx
}
}
```
同理可得 `Actions` 和 `Getters`。<br>
当然 `Getters` 有点区别,他下面的是属性,这并无大碍,构造的时候用 `get` 属性访问器就行了。<br><br>
最后就是 `Store` 类的继承了,`Store`类 需要知道`State`、`Mutations`、`Actions`、`Getters`这四个属性的类型,所以通过泛型来约束:
```typescript
class StoreCuer<
S,
M extends ICuer = ICuer,
A extends ICuer = ICuer,
G extends ICuer = ICuer
> extends Store<S> {
constructor(
state: S,
options?: {
mutations?: M;
actions?: A;
getters?: G;
plugins?: StoreOptions<S>["plugins"];
strict?: StoreOptions<S>["strict"];
}
)
}
```
然后把这些类型对应到相应的属性:
```typescript
class StoreCuer<
S,
M extends ICuer = ICuer,
A extends ICuer = ICuer,
G extends ICuer = ICuer
> extends Store<S> {
readonly commits!: M;
readonly dispatchs!: A;
readonly getters!: G;
}
```
这样就实现了:
```typescript
const store = new StoreCuer();
store.getters.xxx //访问 getter
store.commits.xxx(payload?) //调用 commit
store.dispatchs.xxx(payload?) //调用 dispatch
```
<br>
结构就基本Ok了,剩下的就是代码逻辑的实现了。优化
结构和代码逻辑实现了之后就是
vuex原有的一些如commit、dispatch、subscribe、subscribeAction、mapState、mapGetters等函数添加类型约束了。commit函数的优化先重写
interface Commit//函数通用类型 type Method = (...args: unknown[]) => unknown; //解析函数的参数 type Params<T> = Parameters<Extract<T, Method>>; //继承 `vuex` 的 `Commit` 接口并添加约束 interface CommitEx<M> extends Commit { <K extends keyof M>(key: K, payload: Params<M[K]>[0]): unknown; }在
StoreCuer类中重新定义commit的类型:class StoreCuer< S, M extends ICuer = ICuer, A extends ICuer = ICuer, G extends ICuer = ICuer > extends Store<S> { commit!: CommitEx<M>; }dispatch同理可得mapState函数的优化vuex的mapState是全局的,他取的是vue实例化时的入口中的$store。但是我们既然构造了一个store的类,就可以直接在类中实现mapState。 在这里我实现了两种映射(由于混合类型的约束没有达到我想要的精确约束的效果,如果有好的方法各位大佬可以指点一下),一种是参数是json格式的class StoreCuer< S, M extends ICuer = ICuer, A extends ICuer = ICuer, G extends ICuer = ICuer > extends Store<S> { mapState<V extends Record<keyof V, keyof S>>(keys: V) { return mapValueOfJson<S, V>(keys, k => () => this.state[k]); } }另一种是接收字符串数组的参数
class StoreCuer< S, M extends ICuer = ICuer, A extends ICuer = ICuer, G extends ICuer = ICuer > extends Store<S> { mapStateOfKeys<V extends keyof S>(...keys: V[]) { return mapValueOfKeys<S, V>(keys, k => () => this.state[k]); } }
最后
暂时没有实现
module和 命名空间,因为目前的东西对于我来说是够用的。如果需要统一一个store的入口可以这样写:// store/test1.store.ts 文件 //... export default test1Store; // store/test2.store.ts 文件 //... export default test2Store; // store/index.ts 文件 import test1Store from "./test1.store.ts"; import test2Store from "./test2.store.ts"; export default { test1Store, test2Store };如果要把
index.ts的对象绑定到全局的vue对象上可以这样写:import storeCuer from "./store/index.ts"; Vue.prototype.$storeCuer = storeCuer; //类型约束 declare module 'vue/types/vue' { interface Vue { $storeCuer: typeof storeCuer; } }
用法
class
Mutations```typescript class Mutations extends Cuer.Mutations<ExampleStore> { test(){ this.state.xxx //访问 state this.store.getters.xxx //访问 getter this.xxx //调用当前类的 commit this.store.commit("xxx") //调用 commit this.store.commits.xxx //调用 commit } } ```class
Actions```typescript class Actions extends Cuer.Actions<ExampleStore> { test(){ this.state.xxx //访问 state this.store.getters.xxx //访问 getter this.xxx //调用当前类的 dispatch this.store.commit("xxx", payload?) //调用 commit this.store.commits.xxx(payload?) //调用 commit this.store.dispatch("xxx", payload?) //调用 dispatch this.store.dispatchs.xxx(payload?) //调用 dispatchs } } ```class
Getters```typescript class Getters extends Cuer.Actions<ExampleStore> { //使用访问器来实现 getter get test() { this.state.xxx //访问 state this.getters.xxx //访问 getter this.xxx //访问当前类的 getter return xxx; } } ```class
StoreCuer// `StoreCuer` 继承自 `vuex` 的 `Store`,支持除 `module` 之外的大部分内容 class ExampleStore extends Cuer.StoreCuer< typeof state, Mutations, Actions, Getters > { constructor() { super(state, { mutations: new Mutations(), actions: new Actions(), getters: new Getters() }); } } const store = new ExampleStore(); export default store;
store.state.xxx //访问 state
store.getters.xxx //访问 getter
store.xxx //访问 Store 的函数
store.commit("xxx", payload?) //调用 (优化约束,以强化提示)
store.commits.xxx(payload?) //调用 commit
store.dispatch("xxx", payload?) //调用 (优化约束,以强化提示)
store.dispatchs.xxx(payload?) //调用 dispatch
store.subscribe(fn) // (优化约束,以强化提示)
store.subscribeAction(fn) // (优化约束,以强化提示)
store.mapState({...}) // 映射 state
store.mapStateOfKeys(...) // 映射 state
store.mapGetters({...}) // 映射 getters
store.mapGettersOfKeys(...) // 映射 getters
store.mapActions({...}) // 映射 actions
store.mapActionsOfKeys(...) // 映射 actions
store.mapMutations({...}) // 映射 mutations
store.mapMutationsOfKeys(...) // 映射 mutations
```