Component structure
Component Structure
Components should follow always this order template, script, style
<template>
<div class="title">Awesome component</div>
</template>
<script>
export default {
name: 'AwesomeComponent'
}
</script>
<style lang="postcss">
.title {
color: red;
@apply w-full;
}
</style>
Rules
For a complete list see Vue Style Guide
Naming
Multi-word component names
Component names should always be multi-word and PascalCase, except for root App components.
This prevents conflicts with existing and future HTML elements, since all HTML elements are a single word.
TodoList
Base component names
Since component names should always be multi-word, this convention prevents you from having to choose an arbitrary prefix for simple component wrappers
R64Button, R64Input ...
Single-instance component names
Components that should only ever have a single active instance should begin with the The prefix, to denote that there can be only one.
layout/
|- TheHeading.vue
|- TheSidebar.vue
Tightly coupled component names
Child components that are tightly coupled with their parent should include the parent component name as a prefix.
components/
|- TodoList.vue
|- TodoListItem.vue
|- TodoListItemButton.vue
Order of words in component names
Component names should start with the highest-level (often most general) words and end with descriptive modifying words.
components/
|- SearchButtonClear.vue
|- SearchButtonRun.vue
|- SearchInputQuery.vue
|- SearchInputExcludeGlob.vue
Full-word component names
Component names should prefer full words over abbreviations.
The autocompletion in editors make the cost of writing longer names very low, while the clarity they provide is invaluable. Uncommon abbreviations, in particular, should always be avoided.
components/
|- StudentDashboardSettings.vue
|- UserProfileOptions.vue
Component name casing in templates
Component names should always be PascalCase in single-file components and string templates. PascalCase ensures that your Vue components remain distinctly visible.
kebap-case is allowed for third party libraries that only have kebap-case names registered. In this case we have to add exceptions to the linter
<!-- Local components or libraries that allow PascalCase -->
<TodoListItem />
<!-- Libraries that only allow kebap-case -->
<third-party-checkbox />
Props
Prop definitions
Prop definitions should always be as detailed as possible, specifying at least type(s) and default values. Default value can be avoided if required is set to true.
props: {
status: {
type: String,
default: 'draft'
}
}
Prop name casing
Prop names should always use camelCase during declaration, but kebab-case in templates.
props: {
greetingText: {
type: String,
default: 'hello'
}
}
<WelcomeMessage greeting-text="hi" />
Template
Self-closing components
Components with no content should be self-closing and end with a space
<MyComponent />
Multi-attribute elements
Elements with multiple attributes should span multiple lines, with one attribute per line.
<MyComponent
foo="a"
bar="b"
baz="c"
/>
Simple expressions in templates
Component templates should only include simple expressions, with more complex expressions refactored into computed properties or methods.
// 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(' ')
}
}
Directive shorthands
Directive shorthands (: for v-bind: and @ for v-on:) should be used always
<input
:value="newTodoText"
@click="onClick"
>
Element attribute order
The attributes of elements (including components) should be ordered consistently
<RenderList
v-for="item in items"
:key="item.id"
class="flex cursor-pointer"
@click="select"
/>
<ChildComponent
v-if="true"
id="some-id"
ref="my-child"
v-model="child"
:some-prop-with-bind="item.id"
some-prop-without-bind="hello"
class="flex cursor-pointer"
@click="select"
/>
See in detail Vue Docs
Keyed v-for
Always use key with v-for.
key with v-for is always required on components, in order to maintain internal component state down the subtree.
<ul>
<li
v-for="todo in todos"
:key="todo.id"
>
{{ todo.text }}
</li>
</ul>
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).
<ul>
<li
v-for="user in activeUsers"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
<ul v-if="shouldShowUsers">
<li
v-for="user in users"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
Component Options
Required Name
Give the component always a name
name: 'AwesomeComponent',
Empty lines in component options
When components begin to feel cramped or difficult to read, adding spaces between multi-line properties can make them easier to skim again.
props: {
value: {
type: String,
required: true
},
focused: {
type: Boolean,
default: false
},
label: String,
icon: String
},
computed: {
formattedValue: function () {
// ...
},
inputClasses: function () {
// ...
}
}
Don't use arrow functions for declaring data, computeds or methods
As arrow functions are bound to the parent context, this will not be the Vue instance as you’d expect and this.myMethod will be undefined.
In data, sometimes we want to intialize some property to a prop or a $route param. If we declare it as arrow function the Vue instance would be undefined.
Handlers without arguments must be called without parens
Is there a difference between:
@keyup="fetchData()"
and
@keyup="fetchData"
First call is executing fetchData without passing arguments.
Second call is passing the event handler as first argument (or event payload if its a custom component)
Component options order
Component/instance options should be ordered consistently.
export default {
name: 'AwesomeComponent',
components: {
// Alphabetical Order
ChildComponentA,
ChildComponentB
},
mixins: [
// Alphabetical Order
AwesomeMixin,
IncredibleMixin
],
props: {
awesomenessLevel: {
type: Number,
default: 100
}
},
data() {
return {
yeah: ''
}
},
computed: {
someComputed() {
return 'something'
}
},
watch: {
yeah() {
//
}
},
// Lifecycle Events (in the order they are called)
beforeCreate() {
//
},
created() {
//
},
// ..
destroyed() {
//
},
// And we always close with methods
methods() {
hackTheWorld() {
//
}
}
}
See in detail Vue Docs
Import order
- Vuex imports
- Absolute third party imports
- Local services or mixins
- Local components
import { mapGetters } from 'vuex'
import { format } from 'date-fns'
import MessagesMixin from '@/mixins/messaging'
import MessagesApi from '@/api/messages'
import CoolComponent from '@/components/CoolComponent'