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'