These recommendations should give you assistance to use Vue.js in a progressive and future-orientated way. They are focused around the core of the official Vue.js dependencies, that are the vue-router, vuex and vue itself.
The recommendations differ in their importance. Check the following table to get an overview.
| Keyword | Description |
|---|---|
| Shall | Shall is used in a sense of must. Ignoring it can result into problems |
| Should | Should has a higher weight than may. It is the recommend approach |
| May | May is used in a sense of optional. It doesn't make a difference for the application |
Mutating a property should be as explicit as possible. Violating it could lead to an unwanted property mutation if you need the property as read-only.
computed: {
property () {
return this.$store.state.property
}
}You have two possibilities to improve that.
Use computed getters/setters:
computed: {
property: {
get () {
return this.$store.state.property
},
set (property) {
this.$store.commit('UPDATE_PROPERTY', property)
}
}
}Use Vuex getters:
getters: {
property: state => {
return state.property
}
}Since Vue.js can't detect property additions or deletions, you sould always use Vue.set to add and Vue.delete to delete properties or array keys. This is especially necessary when setting or deleting an array index or an object property. Please note: This won't be necessary in future versions.
In Vue.js, many click directives should be handlers. Handlers do a couple of things. They should call other functions instead of combining the whole logic, which can lead to unpure functions with unwanted side effects. It's pretty easy to overlook that. Given a common example:
setIsVisibleModal (isVisibleModal) {
this.isVisibleModal = isVisibleModal
if (isVisibleModal === false) this.setIsVisibleModalButton(true)
}This function does two things, even so the function name clearly coerces you to do only one thing. It's easier and more testable to use a handler instead, like showed here:
modalHandler (isVisibleModal) {
this.setIsVisibleModal(isVisibleModal)
if (isVisibleModal === false) this.setIsVisibleModalButton(true)
}HTTP requests should be independent from components. The underlying protocol, URI or route may change and you have to rewrite code at multiple places. It is better to have API-related operations at a centralized place instead of doing HTTP requests at the component level.
When you import a component the usual way, it gets loaded upfront, regardless of if it's needed. You can prevent that behavior with a function.
const Component = () => import('./Component')If you need to setup up something during one of the hooks upfront, you should define a method instead of writing everything into the hook. With that, you can easily reinvoke the method later if necessary.
The Vuex is the centralized state of your application. To reason about state mutations, you must be aware of changes in an exact manner. When the strict mode is enabled and the state is mutated outside of a mutation handler, an error will be thrown.
In general, components should be loosely coupled. However, there are situations where components can also be tightly coupled. If you have a dedicated mother-child-relationship, it is perfectly okay to use Vue.parent to access the mother component from the child. Nevertheless, if components can stand on their own, you shouldn't use it. It could be that the relationship get's interrupted and the child component is wrapped around another component which makes Vue.parent useless.
Using deep: true at a watcher leads to heavy calculations because Vue.js has to recursively check for property changes. It's better to use an explicit getter instead and watch for it.
Using entities as component names can heavily simplify reasoning about your application. A component is responsible for doing one thing and the name should reflect that. Therefore, you may end up using names like UserGet, ProductPatch or ProductPost. For common logic use mixins. Furthermore, your states should be an image of your server API. Try avoiding abstraction of your API.
Consider a product page with a container component holding product components with another subcomponent loading product images. Thus, the responsibility chain goes down from the container component to the product component to the product images component. If there is an error with the product images, let the product component handle it. If the product component crashes, let the container decide what to do. This guarantees that the component with the most knowledge makes decisions.
You might find yourself reseting a Vuex state. Instead of setting verbosely every property of the state, define a state constructor instead. To do so, wrap the state into an arrow function and use Object.assign to reset the state.
const stateConstructor = () => ({
entity: ''
})Mutations:
RESET_ENTITY (state) {
Object.assign(state, stateConstructor())
}Most times, a component is a seperated, isolated unit of your application. Therefore, there is no need for such a component to be accessible from the outside or the other way around. You can save a lot of state managament if you ask yourself some questions:
- Do I need the components data or state elsewhere?
- Does an other component must mutate the components behaviour?
- Do I need the component elsewhere?
You may end up putting parts of your component into the store and the rest into the component.
To distinguish between modules and entities, you may use an upper-case module name. If you have many nested modules, this prevents you from mutating the root state, rather than a dedicated object for the entity. To use an upper-case namespace, simply define your modules like this:
new Vuex.Store({
modules: {
User: user,
Product: product,
Basket: basket
}
})Vue.js provides several varying APIs for DOM and state mutations or event communications. Since you decide about the interface names (like methods, events or DOM elements), a consistent naming convention is recommended.
| Entity | Example |
|---|---|
| Component file name | SearchBar |
| Component | <search-bar/> |
| Events | make-user-visible |
| Mixin file name | SearchBarMixin |
| Mixin | SearchBarMixin |
| Props | :is-red="true" |
| References | user-post |
| Entity | Example |
|---|---|
| Route name | DashboardIndex |
| Entity | Example |
|---|---|
| Module file name | user.module |
| Module | User |
| Mutation file name | user.mutations |
| Mutation functions | SET_USER |
| Action file name | user.actions |
| Action functions | postUser |
| State file name | user.state |
| State properties | isUserVisible |