After several years of working on Vue.js projects, I've honed my ability to incorporate top-notch coding principles and practices for crafting clean code. By leveraging these guidelines, we can steer clear of mistakes and embrace optimal coding patterns. It's worth noting that this perspective is founded on my personal experiences, and it may not universally apply to every team or project.
In this blog, I will enumerate a selection of best practices that can significantly enhance our code-writing endeavors.
1. Make sure to use "key" when you're using "v-for."
When you're using "v-for" with components, you should always include a "key." This helps to keep the component's internal information consistent. Even when you're working with regular elements, using "key" is a good idea. It helps ensure things behave predictably, especially when dealing with animations and making sure objects stay the same.
Bad Example
<ul>
<li v-for="list in lists">
{{ todo.text }}
</li>
</ul>
Good Example
<ul>
<li
v-for="list in lists"
:key="list.id"
>
{{ list.text }}
</li>
</ul>
2. Don't mix v-if and v-for
It's not a good idea to use v-if and v-for on the same thing. There are two situations where you might think about doing this:
If you want to show only certain items in a list (like only active users), instead of using v-if, make a new list with just the items you want to show (like activeUsers).
If you want to hide a whole list (for example, if shouldShowUsers is false), put the v-if on a container like an ul or ol instead of using it directly on the v-for loop.
Bad Example
<ul>
<li
v-for="user in users"
v-if="user.isActive"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
Good Example
<ul>
<li
v-for="user in activeUsers"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
<ul>
<template v-for="user in users" :key="user.id">
<li v-if="user.isActive">
{{ user.name }}
</li>
</template>
</ul>
3. Choose component names with multiple words.
When naming your components, it's a good practice to have names with more than one word, except for the main App component. This helps avoid any clashes with existing or future HTML elements, as HTML elements are single words.
Bad Example
<!-- in pre-compiled templates -->
<Item />
<!-- in in-DOM templates -->
<item></item>
Good Example
<!-- in pre-compiled templates -->
<TodoItem />
<!-- in in-DOM templates -->
<todo-item></todo-item>
4. Naming Basics for Components
For components that handle the core design and styles unique to your app (sometimes called presentational, dumb, or pure components), it's a good practice to start their names with a particular prefix, like Base, App, or V.
Bad Example
components/
|- MyButton.vue
|- VueTable.vue
|- Icon.vue
Good Example
components/
|- BaseButton.vue
|- BaseTable.vue
|- BaseIcon.vue
components/
|- AppButton.vue
|- AppTable.vue
|- AppIcon.vue
components/
|- JButton.vue
|- JTable.vue
|- JIcon.vue
5. How to Write Component Names in Templates
In most projects, when you're working with Single-File Components and string templates, your component names should be written in PascalCase. However, when you're using in-DOM templates, you should use kebab-case.
Here's why PascalCase is preferred in most cases:
Editors can help you by suggesting component names because PascalCase is also used in JavaScript.
PascalCase component names, like, look more distinct from single-word HTML elements compared to because there are two noticeable differences (the two capital letters) instead of just one (a hyphen).
If you're using any non-Vue custom elements in your templates, like a web component, using PascalCase ensures that your Vue components stand out clearly.
Bad Example
<!-- In Single-File Components and string templates -->
<mycomponent/>
<!-- In Single-File Components and string templates -->
<myComponent/>
<!-- In in-DOM templates -->
<MyComponent></MyComponent>
Good Example
<!-- In Single-File Components and string templates -->
<MyComponent/>
<!-- In in-DOM templates -->
<my-component></my-component>
6. Prop name casing
When you declare props, always use camelCase. If you're using props in in-DOM templates, then they should be named with kebab-case. However, in Single-File Components templates and JSX, you have the flexibility to use either kebab-case or camelCase for props. It's essential to maintain consistency in your choice of casing throughout your application. In other words, if you opt for camelCase props, avoid using kebab-case ones elsewhere in your code.
Bad Example
const props = defineProps({
'greeting-text': String
})
// for in-DOM templates
<welcome-message greetingText="hi"></welcome-message>
Good Example
const props = defineProps({
greetingText: String
})
<WelcomeMessage greeting-text="hi"/>
// or
<WelcomeMessage greetingText="hi"/>
// for in-DOM templates
<welcome-message greeting-text="hi"></welcome-message>
7. Keep Component Templates Simple
When you're working on component templates, try to use simple expressions. If you find yourself using complicated expressions, it's better to move them to computed properties or methods. This keeps your templates clear and easy to understand. Instead of getting caught up in how things are calculated, you can focus on what should be shown on the screen.
Additionally, using computed properties and methods makes your code more reusable, which is a good thing for keeping your code organized and efficient.
Bad Example
{{
fullName.split(' ').map((word) => {
return word[0].toUpperCase() + word.slice(1)
}).join(' ')
}}
Good Example
<!-- In a template -->
{{ normalizedFullName }}
// The complex expression has been moved to a computed property
const normalizedFullName = computed(() =>
fullName.value
.split(' ')
.map((word) => word[0].toUpperCase() + word.slice(1))
.join(' ')
)
8. Using Shortcut Notations for Directives
When you're working with directives, like v-bind, v-on, and v-slot, you can use shorthand notations like : for v-bind, @ for v-on, and # for v-slot. But here's the rule: always use them or never use them. It's about being consistent in your code. So, pick one approach and stick with it throughout your project.
Bad Example
<input
v-bind:value="newTodoText"
:placeholder="newTodoInstructions"
>
<input
v-on:input="onInput"
@focus="onFocus"
>
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
<template #footer>
<p>Here's some contact info</p>
</template>
Good Example
<input
:value="newTodoText"
:placeholder="newTodoInstructions"
>
<input
v-bind:value="newTodoText"
v-bind:placeholder="newTodoInstructions"
>
<input
@input="onInput"
@focus="onFocus"
>
<input
v-on:input="onInput"
v-on:focus="onFocus"
>
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
<template v-slot:footer>
<p>Here's some contact info</p>
</template>
<template #header>
<h1>Here might be a page title</h1>
</template>
<template #footer>
<p>Here's some contact info</p>
</template>
9. Use detailed prop definitions
When you're writing code that involves props (which are like special instructions you can give to a component), always be as clear as you can about what those props should be like. At the very least, make sure to say what type of data the props should be. This helps everyone understand how to use your code properly and avoids any confusion.
Bad Example
// This is only OK when prototyping
const props = defineProps(['status'])
Good Example
const props = defineProps({
status: String
})
const props = defineProps({
status: {
type: String,
required: true,
validator: (value) => {
return ['syncing', 'synced', 'version-conflict', 'error'].includes(
value
)
}
}
})
10. Connect Child Components to Parents
When you have child components that work closely with a specific parent component, it's a good idea to include the parent's name at the beginning of the child component's name. This naming strategy helps show the strong connection between them. Since files are often sorted alphabetically, it also keeps these related files grouped, making them easier to find and work with.
Bad Example
components/
|- TodoList.vue
|- TodoItem.vue
|- TodoButton.vue
components/
|- SearchSidebar.vue
|- NavigationForSearchSidebar.vue
Good Example
components/
|- TodoList.vue
|- TodoListItem.vue
|- TodoListItemButton.vue
components/
|- SearchSidebar.vue
|- SearchSidebarNavigation.vue
Conclusion 🎉🎭
👏👏 I hope that by reading this far, now you can maintain a well-organized and clear Vue.js codebase, and stick to consistent naming conventions and coding practices. Use PascalCase for component names, camelCase for prop declarations, and kebab-case for in-DOM template props. Keep templates simple with basic expressions and move complex logic into computed properties or methods for readability and reusability. Make parent-child component relationships evident in names when they're closely connected. Lastly, whether you opt for shorthand notations for directives or not, maintain consistency throughout your codebase for clarity. So, I encourage you to try it out and enjoy its benefits.
Please feel free to share your thoughts and opinions with me, and if you encounter any issues or have any questions, don't hesitate to leave a comment.
Keep on hacking! Happy coding! 🚀🌈
Top comments (4)
In
v-if
/v-for
example,activeUsers
is a computed I think ?We may not always calculate the data, making the computed property optional. And thank you for responding :)
Maybe, for me it's best solution.
I prefer a
v-for
on a computed property instead a template with av-if
likev-if="user.isActive"
in second good example.I don't know what is better in performance ;)
Great I will also try doing computed on such situations