1. Use Key With V-For
Vue does the heavy lifting of updating what’s rendered to the screen. But there’s only so much that Vue can know about your app and how it should do these updates.
This is necessary so that Vue can track your component state as well as have a constant reference to your different elements. An example where keys are extremely useful is when using animations or Vue transitions.
You can read a detailed article about “Key attributes in Vue” Here
<!-- Bad -->
<div v-for="product in products"></div>
<!-- Good! -->
<div v-for="product in products" :key="product.id"></div>
2. Use Kebab-Case For Events
It’s always recommended to use kebab-case
for event/vue-attributes.
Example of emit:
this.$emit('close-window');
Example in a tag:
<Tab @close-window="handleEvent()" />
3. Props Definition
In committed code, prop definitions should always be as detailed as possible, specifying at least type(s).
Detailed prop definitions have two advantages:
- They document the API of the component, so that it’s easy to see how the component is meant to be used.
- In development, Vue will warn you if a component is ever provided incorrectly formatted props, helping you catch potential sources of error.
Prop names should always use camelCase
during declaration, but kebab-case
in templates/strings and JSX
Bad:
// This is only OK when prototyping
props: ['status'];
Good:
props: {
status: String;
}
Even Better:
props: {
status: {
type: String,
required: true,
validator: function (value) {
return [
'syncing',
'synced',
'version-conflict',
'error'
].indexOf(value) !== -1
}
}
}
4. Avoid v-if with v-for
Never use v-if
on the same element as v-for
.
There are two common cases where this can be tempting:
- To filter items in a list (e.g.
v-for="user in users" v-if="user.isActive"
). In these cases, replace users with a new computed property that returns your filtered list (e.g.activeUsers
). - To avoid rendering a list if it should be hidden (e.g.
v-for="user in users" v-if="shouldShowUsers"
). In these cases, move the v-if to a container element (e.g. ul, ol).
A smarter solution would be to iterate over a computed property. The above example would look something like this.
<!--Example 1-->
<ul>
<li v-for="user in activeUsers" :key="user.id">{{ user.name }}</li>
</ul>
<!--Example 2-->
<ul v-if="shouldShowUsers">
<li v-for="user in users" :key="user.id">{{ user.name }}</li>
</ul>
5. Single-file Component Filename Casing
Filenames of single-file components should either be always PascalCase
or always kebab-case
.
PascalCase
works best with autocompletion in code editors, as it’s consistent with how we reference components in JS(X) and templates, wherever possible. However, mixed case filenames can sometimes create issues on case-insensitive file systems, which is why kebab-case
is also perfectly acceptable.
Bad:
components/
|- mycomponent.vue
components/
|- myComponent.vue
Good:
components/
|- MyComponent.vue
components/
|- my-component.vue
6. Component File Naming conventions
Child components that are tightly coupled with their parent should include the parent component name as a prefix.
Bad:
components/ |- TodoList.vue |- TodoItem.vue |- TodoButton.vue components/ |- SearchSidebar.vue |-
NavigationForSearchSidebar.vue
Good:
components/ |- TodoList.vue |- TodoListItem.vue |- TodoListItemButton.vue components/ |- SearchSidebar.vue |-
SearchSidebarNavigation.vue
7. Stay consistent with your directive shorthand
A common technique among Vue developers is to use shorthand for directives. For example:
@
is short for v-on::
is short for v-bind#
is short for v-slot
It is great to use these shorthands in your Vue project. But to create some sort of convention across your project, you should either always
use them or never
use them. This will make your project/codebase more cohesive and readable.
8. Self Closing Components
Components with no content should be self-closing in single-file components, string templates, and JSX - but never in DOM templates
.
Components that self-close communicate that they not only have no content, but are meant to have no content. It’s the difference between a blank page in a book and one labeled “This page intentionally left blank.” Your code is also cleaner without the unnecessary closing tag.
Unfortunately, HTML doesn’t allow custom elements to be self-closing - only official “void” elements. That’s why the strategy is only possible when Vue’s template compiler can reach the template before the DOM, then serve the DOM spec-compliant HTML.
Bad:
<!-- In single-file components, string templates, and JSX -->
<MyComponent></MyComponent>
<!-- In DOM templates -->
<my-component />
Good:
<!-- In single-file components, string templates, and JSX -->
<MyComponent />
<!-- In DOM templates -->
<my-component></my-component>
<!-- Or Everywhere -->
<my-component></my-component>
Also note that if you’ve already invested heavily in kebab-case
, consistency with HTML conventions and being able to use the same casing across all your projects may be more important than the advantages listed above. In those cases, using kebab-case everywhere is also acceptable.
9. Multi-Attribute Elements
Elements with multiple attributes should span multiple lines, with one attribute per line.
In JavaScript, splitting objects with multiple properties over multiple lines is widely considered a good convention, because it’s much easier to read.
Bad:
<img src="https://vuejs.org/images/logo.png" alt="Vue Logo" />
<MyComponent foo="a" bar="b" baz="c" />
Good:
<img src="https://vuejs.org/images/logo.png" alt="Vue Logo" />
<MyComponent foo="a" bar="b" baz="c" />
10. Simple Expressions In Templates
Component templates should only include simple expressions, with more complex expressions refactored into computed properties or methods.
Bad:
{{ fullName.split(' ').map(function (word) { return word[0].toUpperCase() + word.slice(1) }).join(' ') }}
Good:
<!-- In a template -->
{{ normalizedFullName }}
// The complex expression has been moved to a computed property
computed: {
normalizedFullName: function () {
return this.fullName.split(' ').map(function (word) {
return word[0].toUpperCase() + word.slice(1)
}).join(' ')
}
}
11. Don’t call a method on created AND watch
A common mistake Vue developers make is they unnecessarily call a method in created and watch. The thought behind this is that we want to run the watch hook as soon as a component is initialized.
Bad:
watch () {
property() {
this.handleChange()
}
},
created: () {
this.handleChange()
},
methods: {
handleChange() {
// stuff happens
}
},
Good:
watch () {
property {
immediate: true
handler() {
this.handleChange()
}
}
},
methods: {
handleChange() {
// stuff happens
}
},
12. Store: Observable vs Vuex
Everyone is inclined to using a full blown store with vuex. Though it’s a good approach, I would like to provide an alternative thought process.
If it’s a small project would recommend using Vue observable, which pretty much works the same as store. And for larger projects, using Vuex should be an obvious choice. Now, how do you decide which is a small project or a large project. The approach that I follow is, if I have less than 4 State objects or less than 2 actions/methods, I use Vue Observable (If it’s really required). If it goes beyond that, would recommend using Vuex for sure!
These set of Vue tips hopefully will make your codebase more maintainable, readable, and more professional.
Hope that you found this article on Vue best-practices helpful. ❤️