1、 前言
看到公司框架中的vuex使用的是vuex-module-decorators扩展模块。使用对象式的调用方式调用vuex的State、Getter、Mutation、Action。好奇实现方式,翻了翻vuex-module-decorators的源码,写一篇文章记录一下vuex-module-decorator的使用方式和个人的一些理解,如果有理解错误的地方希望大家可以指正。
本文会为大家介绍vuex-module-decorators的使用方式,让大家在开发过程中通过vuex-module-decorators更丝滑的使用vuex。
再为大家撕一遍vuex-module-decorators的源码,了解一下vuex-module-decorators的实现方式和思路。让大家在使用过程中避免一些可能遇到的问题,并且在遇到问题的时候可以更方便的定位和解决问题。
希望大家通过阅读本文中的内容可以对vuex和vuex-module-decorators有更深的了解。
2 vuex-module-decorators是什么?
vuex-module-decorators是vuex的一个扩展模块,可以让你像调用对象的方式一样操作vuex,提供类型安全的类型校验和在IDE中自动补全/提醒/快捷定位方法功能。官网地址
4 vuex-module-decorators简介
vuex-module-decorators模块是在vuex原始结构上面,把State、Getter、Mutation、Action、命名空间、动态模块进行了重新封装。 在编写代码的时候像写一个类那样去写vuex。在使用的时候像使用对象那样去使用vuex。
4.1 vuex-module-decorators和vuex定义风格对比
import { VuexModule, Module, Mutation, Action } from 'vuex-module-decorators'
import { get } from 'axios'
interface PostEntity {
comments: string[]
}
@Module
export default class Posts extends VuexModule {
posts: PostEntity[] = [] // initialize empty for now
get totalComments(): number {
return this.posts
.filter(post => {
// Take those posts that have comments
return post.comments && post.comments.length
})
.reduce((sum, post) => {
// Sum all the lengths of comments arrays
return sum + post.comments.length
}, 0)
}
@Mutation
updatePosts(posts: PostEntity[]) {
this.posts = posts
}
@Action({ commit: 'updatePosts' })
async fetchPosts() {
return get('https://jsonplaceholder.typicode.com/posts')
}
}
转换成vuex风格代码
module.exports = {
state: {
posts: []
},
getters: {
totalComments: (state) => {
return state.posts
.filter((post) => {
return post.comments && post.comments.length
})
.reduce((sum, post) => {
return sum + post.comments.length
}, 0)
}
},
mutations: {
updatePosts: (state, posts) => {
// 'posts' is payload
state.posts = posts
}
},
actions: {
fetchPosts: async (context) => {
// the return of the function is passed as payload
const payload = await get('https://jsonplaceholder.typicode.com/posts')
// the value of 'commit' in decorator is the mutation used
context.commit('updatePosts', payload)
}
}
}
调用方式对比
获取state
vuex-module-decorators调用
UserModule.userCode
vuex调用
store.state[name?].userCode
获取getter
vuex-module-decorators调用
SappModule.currentTab
vuex调用
store.getters.[name/?]currentTab
调用Mutation
vuex-module-decorators调用
SappModule.SET_JDEID(eid)
vuex调用
store.commit([name/?]SET_JDEID, eid)
调用Action
vuex-module-decorators调用
SappModule.getMenus()
vuex调用
store.dispatch([name/?]getMenus)
4.2 vuex-module-decorators中定义vuex的状态(State)
vuex-module-decorators定义的类中所有属性都转换为vuex的状态(state)。
官方State文档
import { Module, VuexModule } from 'vuex-module-decorators'
@Module
export default class Vehicle extends VuexModule {
wheels = 2
}
相当于:
export default {
state: {
wheels: 2
}
}
4.3 vuex-module-decorators中定义vuex的Getter
vuex-module-decorators定义的类中所有 ES6 getter 函数都转换为 vuex getter。
官方Getter文档
import { Module, VuexModule } from 'vuex-module-decorators'
@Module
export default class Vehicle extends VuexModule {
wheels = 2
get axles() {
return this.wheels / 2
}
}
相当于:
export default {
state: {
wheels: 2
},
getters: {
axles: (state) => state.wheels / 2
}
}
带参数的getter定义(Method-Style Access):
@Module
export default class Vehicle extends VuexModule {
companies = []
get company() {
return (companyName: string) => { this.companies.find(company => company.name === companyName) };
}
}
4.4 vuex-module-decorators中定义vuex的Mutation
所有用 修饰的函数@Mutation修饰的方法都转换为 Vuex 的mutation。 官方Mutation文档
import { Module, VuexModule, Mutation } from 'vuex-module-decorators'
@Module
export default class Vehicle extends VuexModule {
wheels = 2
@Mutation
puncture(n: number) {
this.wheels = this.wheels - n
}
}
相当于:
export default {
state: {
wheels: 2
},
mutations: {
puncture: (state, payload) => {
state.wheels = state.wheels - payload
}
}
}
用@Mutation装饰器装饰Mutations方法在运行时,会将this设置为state,所以想改变state中的状态:state.item++,很简单this.item++
4.5 vuex-module-decorators中定义vuex的Action
所有装饰的函数@Action修饰的方法都转换为 vuex的action 官方Action文档
import { Module, VuexModule, Mutation, Action } from 'vuex-module-decorators'
import { get } from 'request'
@Module
export default class Vehicle extends VuexModule {
wheels = 2
@Mutation
addWheel(n: number) {
this.wheels = this.wheels + n
}
@Action
async fetchNewWheels(wheelStore: string) {
const wheels = await get(wheelStore)
this.context.commit('addWheel', wheels)
}
}
相当于:
const request = require('request')
export default {
state: {
wheels: 2
},
mutations: {
addWheel: (state, payload) => {
state.wheels = state.wheels + payload
}
},
actions: {
fetchNewWheels: async (context, payload) => {
const wheels = await request.get(payload)
context.commit('addWheel', wheels)
}
}
}
用@Action函数装饰的方法,在运行的时候,被调用的this将具有以下形状 -{...[all fields of state], context}。因此,要从动作的主体内手动提交mutation,只需调用this.context.commit('mutationName', mutPayload)。 PS:下文介绍源码的时候会有介绍this
4.6 vuex-module-decorators装饰器:MutationActions
除了上面介绍的state、getter、mutation、action之外,vuex-module-decorators模块还支持另外一种定义为:MutationActions的装饰器 MutationActions。 MutationActions装饰器会经历以下过程
1.先调用一个action,执行异步脚本。 2.然后再讲action中返回的值,通过commit将结果值提交到store中。
vuex-module-decorators写法
import {VuexModule, Module, MutationAction} from 'vuex-module-decorators'
@Module
class TypicodeModule extends VuexModule {
posts: Post[] = []
users: User[] = []
@MutationAction
async function updatePosts() {
const posts = await axios.get('https://jsonplaceholder.typicode.com/posts')
return { posts }
}
}
相当于:
const typicodeModule = {
state: {
posts: [],
users: []
},
mutations: {
updatePosts: function (state, posts) {
state.posts = posts
}
},
actions: {
updatePosts: async function (context) {
const posts = await axios.get('https://jsonplaceholder.typicode.com/posts')
context.commit('updatePosts', posts)
}
}
}
4.7 vuex-module-decorators中定义vuex的命名空间(Namespaced Modules)
在Class装饰器@Module中通过{ namespaced: true }指定命名空间(Namespaced Modules)
@Module({ namespaced: true, name: 'mm' })
class MyModule extends VuexModule {
wheels = 2
@Mutation
incrWheels(extra: number) {
this.wheels += extra
}
@Action({ root: true, commit: 'setWheels' })
clear() {
return 0
}
get axles() {
return this.wheels / 2
}
}
const store = new Vuex.Store({
modules: {
mm: MyModule
}
})
装饰器中的name字段应与您在创建store时将分配给模块的实际名称相匹配。
{ root: true } 这样虽然在命名空间模块中,但将通过dispatch调用clear方法的时候不需要使用dispatch('mm/clear')进行调用
4.8 vuex-module-decorators中定义vuex的动态模块(Dynamic Modules)
vuex-module-decorators可以简单地通过向@Module装饰器向类传递一些属性{dynamic: true, store, name: 'mm'}来动态注册模块 该过程的一个重要部分是,我们必须首先创建商店,然后将商店传递给模块。Dynamic Modules
第 1 步:创建商店
// @/store/index.ts
import Vuex from 'vuex'
const store = new Vuex.Store({
/*
Ideally if all your modules are dynamic
then your store is registered initially
as a completely empty object
*/
})
步骤 2:创建动态模块
// @/store/modules/MyModule.ts
import store from '@/store'
import {Module, VuexModule} from 'vuex-module-decorators'
@Module({dynamic: true, store, name: 'mm'})
export default class MyModule extends VuexModule {
/*
Your module definition as usual
*/
}
5 vuex-module-decorators源码介绍
5.1 vuex-module-decorators源码结构
module文件夹:@Module装饰器的处理过程
action文件:@Action装饰器的处理过程
config文件:一些公共的配置(原始错误:rawError)
helper文件:一些公关扩展方法
index文件:入口文件,会导出vuex-module-decorators提供的一些装饰器(Module、Action、Mutation、MutationAction)、一些基础类型定义(VuexModule)、一些扩展方法(getModule)、公共定义(config)
moduleoptions文件:ts接口定义文件
mutation文件:@Mutation装饰器处理过程
mutationaction文件:@MutationAction装饰器处理过程
vuexmodule文件:getModule方法处理过程
5.2 vuex-module-decorators原理
通过处理类中定义的属性、getter、@Action修饰器修饰的方法、@Mutation修饰器修饰的方法、@MutationAction修饰器修饰的方法、@Module修饰器修饰的类,包装成vuex的state、getter、action、mutation结构,通过Vuex.Store()或者使用registerModule动态注册成vuex的模块 如果是具名对象(在@Module修饰器中传入name属性),在通过getModule返回的对象中,可以通过访问类的值/方法的时候返回vuex的state/使用commit、dispatch调用vuex的action和mutation
5.3 vuex结构配置
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
5.4 action.ts文件
通过@Action装饰器,包装类方法为vuex调用的时候的action结构,并且保存到类的actions中
/*
* action装饰器,把方法挂载到类的actions属性上面
* @param params
* @returns
*/
function actionDecoratorFactory(params) {
const { commit = undefined, rawError = !!config.rawError, root = false } = params || {}
return function(target, key, descriptor) {
// 获取当前类的constructor
const module = target.constructor
// 判断当前constructor中是否有定义actions属性
if (!module.hasOwnProperty('actions')) {
// 收集actions属性中定义的对象(支持类中直接定义actions属性的写法)
module.actions = Object.assign({}, module.actions)
}
// 获取@Action修饰器修饰的方法
const actionFunction = descriptor.value
// ************************************************************************
// 重新生成一个方法挂载到action上面,vuex中action直接执行的时候调用该方法,入参为vuex的context
const action = async function(context, payload) {}
// ************************************************************************
// 如果是根节点,此处保存成一个包含root和handler的方法,否则只有一个方法
module.actions[key] = root ? { root, handler: action } : action
}
}
/**
* The @Action decorator turns an async function into an Vuex action
*
* @param targetOrParams the module class
* @param key name of the action
* @param descriptor the action function descriptor
* @constructor
*/
export function Action(targetOrParams, key, descriptor) {
if (!key && !descriptor) {
return actionDecoratorFactory(targetOrParams)
} else {
actionDecoratorFactory()(targetOrParams, key, descriptor)
}
}
action方法封装
当vuex的action方法被触发的时候,会调用vuex-module-decorators封装的类的方法,并且把vuex的context绑定到对module类的this中(代码中的:moduleAccessor/thisObj对象),可以通过this.context来进行访问
const action = async function(context, payload) {
try {
let actionPayload = null
// 是否已经处理过的对象,处理过的对象有_genStatic属性
if (module._genStatic) {
const moduleName = getModuleName(module)
// 查找是否为rootGetters上面的值,否则。重新获取Module
const moduleAccessor = context.rootGetters[moduleName] ? context.rootGetters[moduleName] : getModule(module)
// 挂载context到moduleAccessor中,moduleAccessor为getModule获取到的值
moduleAccessor.context = context
// 执行该方法的,获取方法return的值,方法中的对象为context
actionPayload = await actionFunction.call(moduleAccessor, payload)
} else {
const thisObj = { context }
addPropertiesToObject(thisObj, context.state)
addPropertiesToObject(thisObj, context.getters)
actionPayload = await actionFunction.call(thisObj, payload)
}
// 是否存在commit,如果存在commit,则执行commit配置的mutation,入参为action的值
if (commit) {
context.commit(commit, actionPayload)
}
// 返回结果
return actionPayload
} catch (e) {}
}
_genStatic:如果装饰器@Module定义了中name属性,会生成_genStatic变量 如果_genStatic存在,此时执行的actionFunction中的this,指向的是getModule获取到的值+context。此时支持在@Action修饰的方法中直接调用@Mutation修饰的方法 如果_genStatic不存在,此时actionFunction中的this包括state、getters中的值+context。此时不支持在@Action修饰的方法中直接调用@Mutation修饰的方法
在运行时得到的actions结构截图
5.5 mutation.ts文件
通过@Mutation装饰器,包装当前方法为vuex调用的时候的mutation结构,并且保存到类的mutations中
/**
* Mutation装饰器,把方法挂载到类的mutations属性上,注册vuex的时候mutations会直接取mutations的值
* @param target
* @param key
* @param descriptor
*/
export function Mutation(target, key, descriptor) {
// 获取当前类的constructor
const module = target.constructor
// 判断当前constructor中是否有定义mutations属性
if (!module.hasOwnProperty('mutations')) {
// 收集mutations属性中定义的对象(支持类中直接定义mutations属性的写法)
module.mutations = Object.assign({}, module.mutations)
}
// 获取@Mutation修饰器修饰的方法
const mutationFunction = descriptor.value
// 把方法挂载到类的mutations属性上,并且在执行方法的时候,把state作为第一个入参
const mutation = function(state, payload) {
// mutationFunction在执行的时候this指向state
mutationFunction.call(state, payload)
}
module.mutations[key] = mutation
}
mutationFunction.call(state, payload),mutationFunction在调用的时候其中的this指向vuex的state,所以在mutation中只能通过this访问定义的属性,无法访问module类的其他方法
const mutationFunction = descriptor.value
// 把方法挂载到类的mutations属性上,并且在执行方法的时候,把state作为第一个入参
const mutation = function(state, payload) {
mutationFunction.call(state, payload)
}
在运行时得到的mutations结构截图
5.6 module文件夹
把@Module修饰的类的属性封装成state格式 把get封装成getters格式 把@Mutation装饰器装饰的方法封装成mutations格式 把@Action装饰器装饰的方法封装成actions格式 使用registerModule注册vuex的动态模块 通过_genStatic拼装可以直接通过访问对象一样访问vuex的对象
/**
* module装饰器
* @param {*} modOrOpt
* @returns
*/
export function Module(modOrOpt) {
if (typeof modOrOpt === 'function') {
/*
* @Module decorator called without options (directly on the class definition)
*/
moduleDecoratorFactory({})(modOrOpt)
} else {
/*
* @Module({...}) decorator called with options
*/
return moduleDecoratorFactory(modOrOpt)
}
}
moduleDecoratorFactory:@Module修饰符修饰的类处理方法
/**
* class的module装饰器
* @param moduleOptions
* @returns
*/
function moduleDecoratorFactory(moduleOptions) {
return function(constructor) {
const module = constructor
// state处理方法
// ************************************************************************
const stateFactory = () => sf(module)
// ************************************************************************
// 收集module的状态赋值到state
if (!module.state) {
module.state = moduleOptions && moduleOptions.stateFactory ? stateFactory : stateFactory()
}
// 收集所有的getters
if (!module.getters) {
module.getters = {}
}
// 命名空间
if (!module.namespaced) {
module.namespaced = moduleOptions && moduleOptions.namespaced
}
let parentModule = Object.getPrototypeOf(module)
// 是否有继承其他的类
while (parentModule.name !== 'VuexModule' && parentModule.name !== '') {
// 把parentModule中的getter拷贝到module上面
addGettersToModule(module, parentModule)
parentModule = Object.getPrototypeOf(parentModule)
}
// ************************************************************************
// 拷贝类自身get标识的方法到getters
addGettersToModule(module, module)
// ************************************************************************
const modOpt = moduleOptions
if (modOpt.name) {
// ************************************************************************
// 为对象定义_genStatic变量,保存数据,getModule中会获取
Object.defineProperty(constructor, '_genStatic', {})
// ************************************************************************
// 为对象添加名称属性,后续getModuleName会用到
Object.defineProperty(constructor, '_vmdModuleName', {
value: modOpt.name
})
}
if (modOpt.dynamic) {
// ************************************************************************
// 注册vue异步store模块
registerDynamicModule(module, modOpt)
// ************************************************************************
}
return constructor
}
}
在运行时得到的state和getters结构截图
sf => stateFactory 复制module类中的属性封装成vuex的state结构
保留关键字
const reservedKeys = ['actions', 'getters', 'mutations', 'modules', 'state', 'namespaced', 'commit']
// 收集所有的状态
export function stateFactory(module) {
// 获取全部的属性列表
const state = new module.prototype.constructor({})
const s = {}
Object.keys(state).forEach((key) => {
// 过滤保留关键字
if (reservedKeys.indexOf(key) !== -1) {
if (typeof state[key] !== 'undefined') {
throw new Error(
`ERR_RESERVED_STATE_KEY_USED: You cannot use the following
['actions', 'getters', 'mutations', 'modules', 'state', 'namespaced', 'commit']
as fields in your module. These are reserved as they have special purpose in Vuex`
)
}
return
}
// 判断是否有这个值,且不为function,此处收集到s中
if (state.hasOwnProperty(key)) {
if (typeof state[key] !== 'function') {
s[key] = state[key]
}
}
})
return s
}
addGettersToModule 复制module类中的get/getters装饰的方法到vuex的getters结构 获取srcModule的prototype中的所有的属性名称列表 判断属性的描述对象中是否有get/getters方法 封装有get/getters方法到vuex的getters结构中
/**
* 拷贝srcModule对象中的getters到targetModule中
* @param targetModule
* @param srcModule
*/
function addGettersToModule(targetModule, srcModule) {
Object.getOwnPropertyNames(srcModule.prototype).forEach((funcName) => {
// 获取指定的属性信息
// configurable: true
// enumerable: false
// value: ƒ Sapp()
// writable: true
const descriptor = Object.getOwnPropertyDescriptor(
srcModule.prototype,
funcName
)
// 判断当前的对象是否有get方法,如果有,添加到targetModule的getters上面
if (descriptor.get && targetModule.getters) {
// 封装成vuex的getter结构
targetModule.getters[funcName] = function(state, getters, rootState, rootGetters) {
const thisObj = { context: { state, getters, rootState, rootGetters } }
addPropertiesToObject(thisObj, state)
addPropertiesToObject(thisObj, getters)
// get的方法中的this包含:state中的值getters中的值,context
const got = (descriptor.get).call(thisObj)
return got
}
}
})
}
registerDynamicModule
通过上面state、getters、@Action、@Mutation处理过以后,得到vuex的module结构,异步注册组件到vuex中
/**
* 注册异步组件
* @param module
* @param modOpt
*/
function registerDynamicModule(module, modOpt) {
if (!modOpt.name) {
throw new Error('Name of module not provided in decorator options')
}
if (!modOpt.store) {
throw new Error('Store not provided in decorator options when using dynamic option')
}
modOpt.store.registerModule(
modOpt.name, // TODO: Handle nested modules too in future
module,
{ preserveState: modOpt.preserveState || false } // 是否保留之前的state
)
}
@Module处理以后返回的对象截图
5.6 vuexmodule.ts
getModule方法的处理,返回可以通过访问对象属性/方法一样访问vuex的state、getter、action、mutation
import { getModuleName } from './helpers'
/**
* 获取module对象,可以直接调用方法,触发vuex的调用
* @param moduleClass
* @param store
* @returns
*/
export function getModule(moduleClass, store) {
// 获取当前类的name
const moduleName = getModuleName(moduleClass)
// getters上是否存在同名的方法
if (store && store.getters[moduleName]) {
return store.getters[moduleName]
} else if (moduleClass._statics) { // 是否为静态绑定,是否已绑定过,用来缓存,防止重置执行
return moduleClass._statics
}
// ************************************************************************
// 获取moduleDecoratorFactory中添加到对象上面的变量,会返回一个包含store的方法
const genStatic = moduleClass._genStatic
// ************************************************************************
if (!genStatic) {
throw new Error(`ERR_GET_MODULE_NO_STATICS : Could not get module accessor.
Make sure your module has name, we can't make accessors for unnamed modules
i.e. @Module({ name: 'something' })`)
}
// storeModule中保存了vuex的state、getters、mutation、action、store对象。
// storeModule中的属性在访问的时候get被劫持指向vuex的state、getters。方法被指向的时候指向vuex的mutation、action
const storeModule = genStatic(store)
if (store) {
store.getters[moduleName] = storeModule
} else {
// 存储到类的_statics属性里面,后续使用
moduleClass._statics = storeModule
}
return storeModule
}
5.8 getModule方法获取的对象截图
5.9 _genStatic方法
拼接store到statics中
通过staticStateGenerator方法,挂载vuex的state到statics对象的属性上
通过staticGetterGenerator方法,挂载vuex的getters到statics对象的属性上
通过staticMutationGenerator方法,挂载vuex的mutations到statics对象的属性上
通过staticActionGenerators方法,挂载vuex的actions到statics对象的属性上
返回一个拥有store、vuex的state属性列表、vuex的getter属性列表、vuex的mutations方法列表、vuex的actions方法列表的对象
// 为对象定义_genStatic变量,保存数据,getModule中会获取
Object.defineProperty(constructor, '_genStatic', {
value: (store) => {
// 如果有传入store,此处直接使用store,否则使用Module装饰器获取到的store
const statics = { store: store || modOpt.store }
if (!statics.store) {
throw new Error(`ERR_STORE_NOT_PROVIDED: To use getModule(), either the module
should be decorated with store in decorator, i.e. @Module({store: store}) or
store should be passed when calling getModule(), i.e. getModule(MyModule, this.$store)`)
}
// =========== For statics ==============
// ------ state -------,挂载module的state到statics中
staticStateGenerator(module, modOpt, statics)
// ************************************************************************
// ------- getters -------
if (module.getters) {
// 挂载module的getters到statics,此处挂载以后SappModule.xxx可以直接取到statics.store.getters中的值
staticGetterGenerator(module, modOpt, statics)
// ************************************************************************
}
// -------- mutations --------
if (module.mutations) {
// 挂载module的Mutation到statics,此处挂载以后SappModule.xxx()可以直接取调用statics.store.commit
staticMutationGenerator(module, modOpt, statics)
// ************************************************************************
}
// -------- actions ---------
if (module.actions) {
// 挂载module的Action到statics,此处挂载以后SappModule.xxx()可以直接取调用statics.store.dispatch
staticActionGenerators(module, modOpt, statics)
// ************************************************************************
}
return statics
}
})
_genStatic返回结构截图
5.10 staticStateGenerator方法
state可以是一个方法、也可以是一个对象,现在使用的一般都是对象
statics中的属性被访问的时候,去store中的state中查找相应属性的值 如.果存在name,则去获取对应对象下的state
/**
* 获取模块的state挂载到statics
* @param module
* @param modOpt
* @param statics
*/
export function staticStateGenerator(module, modOpt, statics) {
// 获取当前模块的state
const state = modOpt.stateFactory ? module.state() : module.state
Object.keys(state).forEach((key) => {
if (state.hasOwnProperty(key)) {
// If not undefined or function means it is a state value,不挂载undefined和function
if (['undefined', 'function'].indexOf(typeof state[key]) === -1) {
// 代理对象的属性到vuex的state中
Object.defineProperty(statics, key, {
get() {
const path = modOpt.name.split('/')
let data = statics.store.state
for (const segment of path) {
data = data[segment]
}
return data[key]
}
})
}
}
})
}
staticStateGenerator 执行结果
5.11 staticGetterGenerator方法
statics中的属性被访问的时候,如果是查找的store中的getters,去store中的getters中查找相应属性的值,如果有namespaced,则去查找name/key。
/**
* 挂载module的getters到statics,此处挂载以后SappModule.xxx可以直接取到statics.store.getters中的值
* @param module
* @param modOpt
* @param statics
*/
export function staticGetterGenerator(module, modOpt, statics) {
// 挂载statics的key到store的getters中
Object.keys(module.getters).forEach((key) => {
if (module.namespaced) {
Object.defineProperty(statics, key, {
get() {
return statics.store.getters[`${modOpt.name}/${key}`]
}
})
} else {
Object.defineProperty(statics, key, {
get() {
return statics.store.getters[key]
}
})
}
})
}
staticGetterGenerator的执行结果运行截图
5.12 staticMutationGenerator方法
statics中的方法被访问的时候,如果是查找的store中的mutations,去store中的commit提交对应的mutation,如果有namespaced,则去查找name/key。
/**
* 挂载module的Mutation到statics,此处挂载以后SappModule.xxx()可以直接取调用statics.store.commit
* @param module
* @param modOpt
* @param statics
*/
export function staticMutationGenerator(module, modOpt, statics) {
// 获取Mutations装饰器挂载到mutations中的列表
Object.keys(module.mutations).forEach((key) => {
if (module.namespaced) {
// 此处获取到的方法可以直接执行,相当于直接执行方法
statics[key] = function(...args) {
statics.store.commit(`${modOpt.name}/${key}`, ...args)
}
} else {
statics[key] = function(...args) {
statics.store.commit(key, ...args)
}
}
})
}
staticMutationGenerator的运行结果截图
5.13 staticActionGenerators
statics中的方法被访问的时候,如果是查找的store中的actions,去store中的dispatch提交对应的action,如果有namespaced,则去查找name/key。
/**
* 挂载module的Action到statics,此处挂载以后SappModule.xxx()可以直接取调用statics.store.dispatch
* @param module
* @param modOpt
* @param statics
*/
export function staticActionGenerators(module, modOpt, statics) {
Object.keys(module.actions).forEach((key) => {
if (module.namespaced) {
statics[key] = async function(...args) {
return statics.store.dispatch(`${modOpt.name}/${key}`, ...args)
}
} else {
statics[key] = async function(...args) {
return statics.store.dispatch(key, ...args)
}
}
})
}
staticActionGenerators的运行结果截图
getModule运行的结果截图 通过以上步骤就可以获取一个Module,Module的属性代理vuex的state/getter,Module中的方法代理vuex的mutation/action
6 vuex-module-decorators的调用和vuex的调用方式的对比
// 获取state
UserModule.userCode
statics.store.state[name?].userCode
// 获取getter
SappModule.currentTab
statics.store.getters.[name/?]currentTab
// 调用Mutation
SappModule.SET_JDEID(eid)
statics.store.commit([name/?]SET_JDEID, eid)
// 调用Action
SappModule.getMenus()
statics.store.dispatch([name/?]getMenus)
通过分析源代码,大家可以了解vuex-module-decorators的具体实现过程。通过装饰器,让vuex的state、getter、mutation、action调用方式代理到一个对象上面。 使用vuex的时候像使用对象的属性/方法一样丝滑。
优点:
在使用过程中调用方式简单方便。保留vuex的特性,比如:getModule中的属性都是响应式的。 并且可以类型安全的类型校验和在IDE中自动补全/提醒/快捷定位方法功能。
缺点:
改变了vuex的特性,可以直接对getModule获取的对象属性进行赋值。 在@Mutation装饰的方法中不允许调用其他的@Mutation,但是可以在@Action装饰的方法中调用其他的Mutation和Action,行为方式统一。
在不同类型装饰的方法中的this指向不同,再使用的时候可能会有歧义。
7 总结
vuex-module-decorators可以满足我们在日常开发中大部分使用vuex的场景,只要在使用的时候注意一下某些边界条件。可以大大提升我们的开发效率和开发体验。