Vuex 4源码学习笔记 - 学习Vuex的TS .d.ts类型描述(十三)

在上一篇笔记中:Vuex 4源码学习笔记 - 通过Vuex源码学习TypeScript类型测试(十二)

我们看到了Vuex的类型声明文件,我们知道Vuex的源码是使用JavaScript来开发的,JS并没有类型的声明,那么Vuex要实现TypeScript的类型就只能通过TypeScript的.d.ts来去描述类型。

那么今天我们来看看Vuex是如何通过.d.ts去描述Vuex的类型。我们会结合它的源码来看

首先来看到的就是createStore使用方法,它接受下面的一些参数

import { createStore } from 'vuex'

export default createStore({
  state: { /**...**/ },
  getters: { /**...**/ },
  actions: { /**...**/ },
  mutations: { /**...**/ },
  modules: { /**...**/ },
  plugins: [],
  strict: false,
  devtools: false,
})

再来看到它的源码,实际是new了一个Store对象

export function createStore (options) {
  return new Store(options)
}

我们再来看它的TS类型定义

export function createStore<S>(options: StoreOptions<S>): Store<S>;

这里主要有三个地方,<S>StoreOptionsStore

  • <S>为一个泛型
  • StoreOptions 为我们传入的参数
  • Store 为Store类

我们看到createStore实际就到new了一个Store对象,我们来先看Store的源代码

export class Store {
  constructor (options = {}) {
    //...
  }

  //...
  
  get state () {
    return this._state.data
  }

  set state (v) {
    //...
  }
  
  //...
}

然后再来看对应的类型定义,首先我们会发现以下划线开头的一些私有属性和一些私有方法并没有出现在类型声明的TS中,因为我们实际在使用Vuex的时候并不涉及到操作这些私有属性和方法。

export declare class Store<S> {
  constructor(options: StoreOptions<S>);

  readonly state: S;
  readonly getters: any;

}

首先来看构造函数中的StoreOptions的类型声明

export interface StoreOptions<S> {
  state?: S | (() => S);
  getters?: GetterTree<S, S>;
  actions?: ActionTree<S, S>;
  mutations?: MutationTree<S>;
  modules?: ModuleTree<S>;
  plugins?: Plugin<S>[];
  strict?: boolean;
  devtools?: boolean;
}
  • ?:表示该字短并非是必传的

State

state的类型可以为泛型S,或者是一个函数,返回值为泛型S。

比如我们的state的可以为:

const state = {
  count: 0
}

那就S就是:

{
  count: 0
}

Getters

getters也是非必传,类型为GetterTree<S, S>,我们找到GetterTree的定义

export interface GetterTree<S, R> {
  [key: string]: Getter<S, R>;
}
  • 指定对象的key为string类型
  • 值为Getter<S, R>类型,我们接着找到Getter
export type Getter<S, R> = (state: S, getters: any, rootState: R, rootGetters: any) => any;

可以看到Getter是一个函数,S和R这两个泛型分别为,S为当前模块的状态,R为根模块的状态,如果没有子模块的话,两个泛型的值其实是一样的。

一般我们的Getter会这样写:

const getters = {
  evenOrOdd: state => state.count % 2 === 0 ? 'even' : 'odd'
}

由于没办法知道用户写的Getter函数到底返回什么类型,所以Getter的返回值为any。

Actions

类型为ActionTree<S, S>,同样去找ActionTree

export interface ActionTree<S, R> {
  [key: string]: Action<S, R>;
}

与Getter很类似,同样找到Action

export type Action<S, R> = ActionHandler<S, R> | ActionObject<S, R>;

Action稍微有些一样,Action有两种类型,一种为ActionHandler、一种是ActionObject。

ActionHandler:

export type ActionHandler<S, R> = (this: Store<R>, injectee: ActionContext<S, R>, payload?: any) => any;

ActionObject:

export interface ActionObject<S, R> {
  root?: boolean;
  handler: ActionHandler<S, R>;
}

但实际ActionObject的handler还是ActionHandler类型的,也就是一个函数,返回值同样为any

一般我们使用Action的方式为:

export const sendMessage = ({ commit }, payload) => {
  api.createMessage(payload, message => {
    commit('receiveMessage', message)
  })
}

export const switchThread = ({ commit }, payload) => {
  commit('switchThread', payload)
}

Mutations

类型为MutationTree<S>,同样去找MutationTree

export interface MutationTree<S> {
  [key: string]: Mutation<S>;
}

与Getter类似,同样去找到Mutation

export type Mutation<S> = (state: S, payload?: any) => any;

Mutation类型很简单,就是一个函数类型。

平时我们使用mutation的代码:

const mutations = {
  increment (state) {
    state.count++
  },
  decrement (state) {
    state.count--
  }
}

Modules

同样,去找 ModuleTree<S>

export interface ModuleTree<R> {
  [key: string]: Module<any, R>;
}

然后找到Module的类型

export interface Module<S, R> {
  namespaced?: boolean;
  state?: S | (() => S);
  getters?: GetterTree<S, R>;
  actions?: ActionTree<S, R>;
  mutations?: MutationTree<S>;
  modules?: ModuleTree<R>;
}

这里面Module其实就形成了递归的引用,modules还是ModuleTree类型。

一般我们使用modules的方式

const nested = {
  namespaced: true,
  state: () => ({
    foo: 'bar'
  }),
  getters: {
    twoBars: state => state.foo.repeat(2)
  }
}

const cart = {
  namespaced: true,
  state,
  getters,
  actions,
  mutations,
  modules: {
    nested
  }
}

const store =  {
  modules: {
    cart,
  },
}

Plugin

同样找到Plugin<S>[],为一个函数格式的数组

export type Plugin<S> = (store: Store<S>) => any;

插件的使用方式:

import { createLogger } from 'vuex'
import { STORAGE_KEY } from './mutations'

const localStoragePlugin = store => {
  store.subscribe((mutation, { todos }) => {
    window.localStorage.setItem(STORAGE_KEY, JSON.stringify(todos))
  })
}

export default process.env.NODE_ENV !== 'production'
  ? [createLogger(), localStoragePlugin]
  : [localStoragePlugin]

strict & devtools

strict和devtools比较简单,都是布尔值。

最后,感觉大家的阅读。

一起学习更多前端知识,微信搜索【小帅的编程笔记】,每天更新

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值