vuex-module-decorators
TypeScript/ES7 Decorators to create Vuex modules declaratively
Top Related Projects
🗃️ Centralized State Management for Vue.js.
Binding helpers for Vuex and vue-class-component
Quick Overview
Vuex-module-decorators is a TypeScript/ES7 decorator library for Vuex, designed to simplify the process of creating and managing Vuex modules in Vue.js applications. It provides a more intuitive and type-safe way to define Vuex modules using decorators, making the code more readable and maintainable.
Pros
- Enhances type safety when working with Vuex in TypeScript projects
- Simplifies the creation and management of Vuex modules
- Improves code readability and maintainability
- Reduces boilerplate code typically associated with Vuex modules
Cons
- Requires TypeScript or Babel setup for decorator support
- Learning curve for developers unfamiliar with decorators
- May introduce additional complexity for simple Vuex stores
- Limited flexibility compared to traditional Vuex module definitions
Code Examples
- Defining a Vuex module using decorators:
import { Module, VuexModule, Mutation, Action } from 'vuex-module-decorators'
@Module({ namespaced: true, name: 'user' })
class UserModule extends VuexModule {
private name: string = ''
@Mutation
setName(newName: string) {
this.name = newName
}
@Action
updateName(newName: string) {
this.context.commit('setName', newName)
}
get userName() {
return this.name
}
}
- Accessing the module in a Vue component:
import { Component, Vue } from 'vue-property-decorator'
import { getModule } from 'vuex-module-decorators'
import UserModule from '@/store/modules/user'
@Component
export default class MyComponent extends Vue {
private userModule = getModule(UserModule, this.$store)
mounted() {
console.log(this.userModule.userName)
this.userModule.updateName('John Doe')
}
}
- Using dynamic modules:
import { Module, VuexModule, Mutation, Action } from 'vuex-module-decorators'
import store from '@/store'
@Module({ dynamic: true, store, name: 'dynamicModule' })
class DynamicModule extends VuexModule {
count: number = 0
@Mutation
increment() {
this.count++
}
@Action
incrementAsync() {
setTimeout(() => {
this.context.commit('increment')
}, 1000)
}
}
Getting Started
-
Install the package:
npm install vuex-module-decorators
-
Enable decorator support in your
tsconfig.json
:{ "compilerOptions": { "experimentalDecorators": true } }
-
Create a Vuex module using decorators:
import { Module, VuexModule, Mutation, Action } from 'vuex-module-decorators' @Module({ namespaced: true, name: 'myModule' }) export default class MyModule extends VuexModule { // Define your state, mutations, actions, and getters here }
-
Use the module in your Vuex store:
import Vue from 'vue' import Vuex from 'vuex' import MyModule from './modules/myModule' Vue.use(Vuex) export default new Vuex.Store({ modules: { myModule: MyModule } })
Competitor Comparisons
🗃️ Centralized State Management for Vue.js.
Pros of Vuex
- Official Vuex library, ensuring long-term support and compatibility
- Simpler setup and configuration for basic use cases
- More extensive documentation and community resources
Cons of Vuex
- More verbose syntax for defining and using modules
- Lacks built-in TypeScript support, requiring additional type definitions
- Can be less intuitive for developers coming from object-oriented backgrounds
Code Comparison
Vuex:
const store = new Vuex.Store({
state: { count: 0 },
mutations: {
increment(state) { state.count++ }
}
})
vuex-module-decorators:
@Module
class CounterModule extends VuexModule {
count = 0
@Mutation
increment() { this.count++ }
}
Key Differences
- vuex-module-decorators provides a more object-oriented approach to Vuex
- It offers better TypeScript integration with decorators
- Allows for cleaner and more modular code structure
- May have a steeper learning curve for developers unfamiliar with decorators
Use Cases
- Vuex: Ideal for smaller projects or teams more comfortable with traditional Vuex syntax
- vuex-module-decorators: Better suited for larger, TypeScript-based projects seeking improved code organization
Community and Maintenance
- Vuex: Larger community, more frequent updates, and official Vue.js support
- vuex-module-decorators: Smaller but active community, regular updates, and good documentation
Binding helpers for Vuex and vue-class-component
Pros of vuex-class
- Simpler syntax for basic Vuex usage
- Easier to understand for developers new to decorators
- Lightweight with fewer dependencies
Cons of vuex-class
- Less flexibility for complex Vuex modules
- Limited support for namespaced modules
- Fewer features compared to vuex-module-decorators
Code Comparison
vuex-class:
@Component
export class MyComponent extends Vue {
@State('count') count!: number
@Mutation('increment') increment!: () => void
}
vuex-module-decorators:
@Module({ namespaced: true, name: 'myModule' })
class MyModule extends VuexModule {
count = 0
@Mutation
increment() {
this.count++
}
}
vuex-class provides a more straightforward approach for simple Vuex usage, while vuex-module-decorators offers more advanced features and better support for complex, namespaced modules. The choice between the two depends on the project's complexity and the team's familiarity with decorators and Vuex patterns.
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
vuex-module-decorators
Detailed Guide: https://championswimmer.in/vuex-module-decorators/
Typescript/ES7 Decorators to make Vuex modules a breeze
Patrons
While I have a day job and I really maintain open source libraries for fun, any sponsors are extremely graciously thanked for their contributions, and goes a long way ð â¤ï¸
CHANGELOG
-
There are major type-checking changes (could be breaking) in v0.9.7
-
There are major usage improvements (non backwards compatible) in 0.8.0
Please check CHANGELOG
Examples
Read the rest of the README to figure out how to use, or if you readily want to jump into a production codebase and see how this is used, you can check out -
- https://github.com/Armour/vue-typescript-admin-template
- https://github.com/xieguangcai/vue-order-admin
- https://github.com/coding-blocks-archives/realworld-vue-typescript
Installation
npm install -D vuex-module-decorators
Babel 6/7
NOTE This is not necessary for
vue-cli@3
projects, since@vue/babel-preset-app
already includes this plugin
- You need to install
babel-plugin-transform-decorators
TypeScript
- set
experimentalDecorators
to true - For reduced code with decorators, set
importHelpers: true
intsconfig.json
- (only for TypeScript 2) set
emitHelpers: true
intsconfig.json
Configuration
Using with target: es5
NOTE Since version
0.9.3
we distribute as ES5, so this section is applicable only to v0.9.2 and below
This package generates code in es2015
format. If your Vue project targets ES6 or ES2015 then
you need not do anything. But in case your project uses es5
target (to support old browsers), then
you need to tell Vue CLI / Babel to transpile this package.
// in your vue.config.js
module.exports = {
/* ... other settings */
transpileDependencies: ['vuex-module-decorators']
}
Usage
The conventional old & boring way
Remember how vuex modules used to be made ?
const moduleA = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: { ... },
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
Hello Decorators !
Well not anymore. Now you get better syntax. Inspired by vue-class-component
import { Module, VuexModule, Mutation, Action } from 'vuex-module-decorators'
@Module
export default class Counter2 extends VuexModule {
count = 0
@Mutation
increment(delta: number) {
this.count += delta
}
@Mutation
decrement(delta: number) {
this.count -= delta
}
// action 'incr' commits mutation 'increment' when done with return value as payload
@Action({ commit: 'increment' })
incr() {
return 5
}
// action 'decr' commits mutation 'decrement' when done with return value as payload
@Action({ commit: 'decrement' })
decr() {
return 5
}
}
async MutationAction === magic
Want to see something even better ?
import { Module, VuexModule, MutationAction } from 'vuex-module-decorators'
import { ConferencesEntity, EventsEntity } from '@/models/definitions'
@Module
export default class HGAPIModule extends VuexModule {
conferences: Array<ConferencesEntity> = []
events: Array<EventsEntity> = []
// 'events' and 'conferences' are replaced by returned object
// whose shape must be `{events: [...], conferences: [...] }`
@MutationAction({ mutate: ['events', 'conferences'] })
async fetchAll() {
const response: Response = await getJSON('https://hasgeek.github.io/events/api/events.json')
return response
}
}
Automatic getter detection
@Module
class MyModule extends VuexModule {
wheels = 2
@Mutation
incrWheels(extra) {
this.wheels += extra
}
get axles() {
return this.wheels / 2
}
}
this is turned into the equivalent
const module = {
state: { wheels: 2 },
mutations: {
incrWheels(state, extra) {
state.wheels += extra
}
},
getters: {
axles: (state) => state.wheels / 2
}
}
Parameters inside a getter
In order to handle parameters, simply return a function like so:
get getUser() {
return function (id: number) {
return this.users.filter(user => user.id === id)[0];
}
}
Putting into the store
Use the modules just like you would earlier
import Vue from 'nativescript-vue'
import Vuex, { Module } from 'vuex'
import counter from './modules/Counter2'
import hgapi from './modules/HGAPIModule'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {},
modules: {
counter,
hgapi
}
})
Module re-use, use with NuxtJS
If you need to support module reuse
or to use modules with NuxtJS, you can have a state factory function generated instead
of a static state object instance by using stateFactory
option to @Module
, like so:
@Module({ stateFactory: true })
class MyModule extends VuexModule {
wheels = 2
@Mutation
incrWheels(extra) {
this.wheels += extra
}
get axles() {
return this.wheels / 2
}
}
this is turned into the equivalent
const module = {
state() {
return { wheels: 2 }
},
mutations: {
incrWheels(state, extra) {
state.wheels += extra
}
},
getters: {
axles: (state) => state.wheels / 2
}
}
Dynamic Modules
Vuex allows us to register modules into store at runtime after store is constructed. We can do the following to create dynamic modules
interface StoreType {
mm: MyModule
}
// Declare empty store first
const store = new Vuex.Store<StoreType>({})
// Create module later in your code (it will register itself automatically)
// In the decorator we pass the store object into which module is injected
// NOTE: When you set dynamic true, make sure you give module a name
@Module({ dynamic: true, store: store, name: 'mm' })
class MyModule extends VuexModule {
count = 0
@Mutation
incrCount(delta) {
this.count += delta
}
}
If you would like to preserve the state e.g when loading in the state from vuex-persist
...
-- @Module({ dynamic: true, store: store, name: 'mm' })
++ @Module({ dynamic: true, store: store, name: 'mm', preserveState: true })
class MyModule extends VuexModule {
...
Or when it doesn't have a initial state and you load the state from the localStorage
...
-- @Module({ dynamic: true, store: store, name: 'mm' })
++ @Module({ dynamic: true, store: store, name: 'mm', preserveState: localStorage.getItem('vuex') !== null })
class MyModule extends VuexModule {
...
Accessing modules with NuxtJS
There are many possible ways to construct your modules. Here is one way for drop-in use with NuxtJS (you simply need to add your modules to ~/utils/store-accessor.ts
and then just import the modules from ~/store
):
~/store/index.ts
:
import { Store } from 'vuex'
import { initialiseStores } from '~/utils/store-accessor'
const initializer = (store: Store<any>) => initialiseStores(store)
export const plugins = [initializer]
export * from '~/utils/store-accessor'
~/utils/store-accessor.ts
:
import { Store } from 'vuex'
import { getModule } from 'vuex-module-decorators'
import example from '~/store/example'
let exampleStore: example
function initialiseStores(store: Store<any>): void {
exampleStore = getModule(example, store)
}
export { initialiseStores, exampleStore }
Now you can access stores in a type-safe way by doing the following from a component or page - no extra initialization required.
import { exampleStore } from '~/store'
...
someMethod() {
return exampleStore.exampleGetter
}
Using the decorators with ServerSideRender
When SSR is involved the store is recreated on each request. Every time the module is accessed
using getModule
function the current store instance must be provided and the module must
be manually registered to the root store modules
Example
// store/modules/MyStoreModule.ts
import { Module, VuexModule, Mutation } from 'vuex-module-decorators'
@Module({
name: 'modules/MyStoreModule',
namespaced: true,
stateFactory: true,
})
export default class MyStoreModule extends VuexModule {
public test: string = 'initial'
@Mutation
public setTest(val: string) {
this.test = val
}
}
// store/index.ts
import Vuex from 'vuex'
import MyStoreModule from '~/store/modules/MyStoreModule'
export function createStore() {
return new Vuex.Store({
modules: {
MyStoreModule,
}
})
}
// components/Random.tsx
import { Component, Vue } from 'vue-property-decorator';
import { getModule } from 'vuex-module-decorators';
import MyStoreModule from '~/store/modules/MyStoreModule'
@Component
export default class extends Vue {
public created() {
const MyModuleInstance = getModule(MyStoreModule, this.$store);
// Do stuff with module
MyModuleInstance.setTest('random')
}
}
Configuration
There is a global configuration object that can be used to set options across the whole module:
import { config } from 'vuex-module-decorators'
// Set rawError to true by default on all @Action decorators
config.rawError = true
Top Related Projects
🗃️ Centralized State Management for Vue.js.
Binding helpers for Vuex and vue-class-component
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot