These conventions are based on the official Vue.js style guide. Where a choice is given between various options, this document will explicitly state which one to pick within the IxDF codebases.
[[toc]]
User component names should always be multi-word, except for root App components. This prevents conflicts with existing and future HTML elements, since all HTML elements are a single word.
Bad
<item />
Good
<todo-item />
Prop definitions should always be as detailed as possible.
Bad
const props = defineProps(['status']);
Good
// With TypeScript (recommended)
const props = defineProps<{
status: string
}>()
// Without TypeScript
const props = defineProps({
status: {
type: String,
required: true,
}
})
A key with v-for is always required on components, in order to maintain internal component state down the subtree.
Bad
<ul>
<li v-for="todo in todos">{{ todo.text }}</li>
</ul>
Good
<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.
Bad
<ul>
<li v-for="user in users" v-if="user.isActive" :key="user.id">{{ user.name }}</li>
</ul>
Good
<ul>
<template v-for="user in users" :key="user.id">
<li v-if="user.isActive">{{ user.name }}</li>
</template>
</ul>
Bad
components/
|- mycomponent.vue
|- myComponent.vue
Good
|- MyComponent.vue
Foundational components, such as buttons, tables or icons that get used within more specific components should all begin with the 'Base' prefix. This ensures they don't clash with HTML element names and also ensures that they are neatly organized together in the codebase.
Bad
components/
|- Button.vue
|- Table.vue
|- Icon.vue
Good
components/
|- BaseButton.vue
|- BaseTable.vue
|- BaseIcon.vue
Child components that are tightly coupled with their parent should include the parent component name as a prefix.
If a component only makes sense in the context of a single parent component, that relationship should be evident in its name. Since editors typically organize files alphabetically, this also keeps these related files next to each other.
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
Component names should start with the highest-level (often most general) words and end with descriptive modifying words. This helps make searching for specific components easier and keeps the codebase neatly organised.
Bad
components/
|- ClearSearchButton.vue
|- ExcludeFromSearchInput.vue
|- LaunchOnStartupCheckbox.vue
|- RunSearchButton.vue
|- SearchInput.vue
|- TermsCheckbox.vue
Good
components/
|- SearchButtonClear.vue
|- SearchButtonRun.vue
|- SearchInputQuery.vue
|- SearchInputExcludeGlob.vue
|- SettingsCheckboxTerms.vue
|- SettingsCheckboxLaunchOnStartup.vue
Components should always be PascalCase in both .vue and .js files. This has a few advantages over kebab-case:
- Editors can autocomplete component names in templates, because PascalCase is also used in JavaScript.
<MyComponent>
is more visually distinct from a single-word HTML element than<my-component>
, because there are two character differences (the two capitals), rather than just one (a hyphen).- If you use any non-Vue custom elements in your templates, such as a web component, PascalCase ensures that your Vue components remain distinctly visible.
Bad
<my-component />
Good
<MyComponent />
Don't use abbreviations in component names.
Bad
components/
|- SdSettings.vue
|- UProfOpts.vue
Good
components/
|- StudentDashboardSettings.vue
|- UserProfileOptions.vue
Always use camelCase for prop declarations and within templates
const props = defineProps({
greetingText: String,
});
<WelcomeMessage greetingText="hi" />
Elements with multiple attributes should span multiple lines, with one attribute per line.
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" />
Component templates should only include simple expressions, with more complex expressions refactored into computed properties or methods.
Bad
{{
fullName.split(' ').map((word) => {
return word[0].toUpperCase() + word.slice(1)
}).join```(' ')
}}
Good
<!-- 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(' ')
)
Complex computed properties should be split into as many simpler properties as possible.
Bad
const price = computed(() => {
const basePrice = manufactureCost.value / (1 - profitMargin.value);
return basePrice - basePrice * (discountPercent.value || 0);
});
Good
const basePrice = computed(() => manufactureCost.value / (1 - profitMargin.value));
const discount = computed(() => basePrice.value * (discountPercent.value || 0));
const finalPrice = computed(() => basePrice.value - discount.value);
Non-empty HTML attribute values should always be inside quotes.
Bad
<input type="text" /> <AppSidebar :style={width:sidebarWidth+'px'}>
Good
<input type="text" /> <AppSidebar :style="{ width: sidebarWidth + 'px' }"></AppSidebar>
Within the IxDF codebases, directive shorthands (:
for v-bind:
, @
for v-on:
and #
for v-slot
) should always be used. The rule is to either always use directive shorthands, or never - don't mix and match. We've chosen to use them.
Bad
<input v-bind:value="newTodoText" v-bind:placeholder="newTodoInstructions" />
<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>
Good
<input :value="newTodoText" :placeholder="newTodoInstructions" />
<input @input="onInput" @focus="onFocus" />
<template #header>
<h1>Here might be a page title</h1>
</template>
<template #footer>
<p>Here's some contact info</p>
</template>
Single-File Components should always order <script>
, <template>
, and <style>
tags consistently, with <style>
last, because at least one of the other two is always necessary.
Bad
<!-- ComponentA.vue -->
<template>...</template>
<script>
/* ... */
</script>
<style>
/* ... */
</style>
Good
<!-- ComponentA.vue -->
<script>
/* ... */
</script>
<template>...</template>
<style>
/* ... */
</style>