Make your VueJS code look more fabulous with class decorator (part 2)
In part 1 of this article, I introduced you to how to make your Vue code look more fabulous than before what you did by using decorators. But in reality, most projects usually use the store management library like Vuex for complexity state through all projects. So, if we just use the decorators for the main code will not be enough. Let's complete this mission.
How to use it?
Firstly, you must already have a project using Vue 3. Then download the package from NPM:
npm install vuex-module-decorators
ES5 transpilation
This package is distributed with ES2015 style code. This means it is perfectly fine for modern browsers (Chrome, Firefox, Safari), but will not work on IE11. If you are using IE11 you are probably having a Babel-based transpilation setup to generate ES5 code. If that's the case, you need to make sure this package also gets transpiled.
Add this in your vue.config.js
(Vue CLI v3):
module.exports = {
transpileDependencies: ['vuex-module-decorators'],
}
Transpiler configurations
- Babel 6/7 - You need to install
babel-plugin-transform-decorators
- Set
experimentalDecorators
: true in yourts.config.json
- For reduced code with decorators, set
importHelpers: true
- Set
emitHelpers: true
(only for Typescript 2)
The features and performances
- Typescript classes with strict type safety: create modules where nothing can go wrong. Compile time type-checking ensures you cannot mutate data not part of the module, or access unavailable fields.
- Decorators for declarative code: annotate your functions with @Action or @Mutation to automatically turn then into Vuex module methods.
- Autocomplete for actions and mutations: The shape of modules is fully typed, so you can access action and mutation functions with type-safety and get autocomplete help.
Get started
Define a module
To define a module, create a class that extends from VuexModule
and must be
decorated with Module
decorator:
import { Module, VuexModule } from 'vuex-module-decorators'
@Module
export default class MyModule extends VuexModule {
someField: string = 'somedata'
}
There is a Module
class in the vuex
package too, which is not a decorator.
Make sure you import the correct Module
decorator from
vuex-module-decorators
Use in store
In your store, you use the MyModule class itself as a module.
import Vuex from 'vuex'
import MyModule from '~/store/mymodule'
const store = new Vuex.Store({
modules: {
myMod: MyModule,
},
})
The way we use the MyModule
class is different from classical
object-oriented programming and similar to how vue-class-component
works. We
use the class itself as a module, not an object constructed by the class.
Access state
All the usual ways of accessing the module work:
- Import from store
import store from '~/store'
...
store.state.myMod.someField
- Use this.$store if in component
this.$store.state.myMod.someField
- Use getModule() to create a type-safe accessor
import { Module, VuexModule, getModule } from 'vuex-module-decorators'
import store from '@/store'
@Module({ dynamic: true, store, name: 'mymod' })
class MyModule extends VuexModule {
someField: number = 10
}
const myMod = getModule(MyModule)
myMod.someField //works
myMod.someOtherField //Typescript will error, as a field doesn't exist
Core concept
State
All properties of the class are converted into state props.
import { Module, VuexModule } from 'vuex-module-decorators'
@Module
export default class Vehicle extends VuexModule {
wheels = 2
}
Getters
All ES6 getter functions of the class are converted into vuex getters.
import { Module, VuexModule } from 'vuex-module-decorators'
@Module
export default class Vehicle extends VuexModule {
wheels = 2
get axles() {
return this.wheels / 2
}
}
Mutations
All functions decorated with @Mutation
are converted into Vuex mutations.
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
}
}
Actions
All functions that are decorated with @Action
are converted into Vuex actions.
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)
}
}
MutationActions
If you have understood how Actions
and Mutations
work you might have
requirements for some functions that:
- Firstly, do an asynchronous action.
- Then commit the resultant value to the store via a mutation.
This is where a @MutationAction
comes into the picture.
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 }
}
}
Note that if S denotes the type of state, then the object returned from a MutationAction
function must be of type Partial<S> The keys present inside the return value (for eg, here posts) are replaced into the store. When a MutationAction
function returns undefined, the mutation part of the MutationAction
will not be called, and the state will remain the same.
Conclusion
Now, we finished the complete style to create Vue 3.0 project using decorator in Vue code and state management. I hope it could help you find out a new way to implement your next project in the future. You can reference official documentation for further information and advanced topics at:
Reference resources
Vue-module-decorator documentation
© Bùi Quốc Khải.