Skip to content

Latest commit

History

History
401 lines (285 loc) 路 7.73 KB

conventions--vue.md

File metadata and controls

401 lines (285 loc) 路 7.73 KB

Vue.js conventions

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]]

Use multi-word component names

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 />

Always add detailed prop definitions

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,
    }
})

Add a key when using v-for

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>

Don't use v-if with v-for

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>

Component files should be named with PascalCase

Bad

components/
|- mycomponent.vue
|- myComponent.vue

Good

|- MyComponent.vue

Append "Base" prefix to any foundational components

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

Tightly coupled component names

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

Order of words in component names

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

Component Name Casing

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 />

Full-word component names

Don't use abbreviations in component names.

Bad

components/
|- SdSettings.vue
|- UProfOpts.vue

Good

components/
|- StudentDashboardSettings.vue
|- UserProfileOptions.vue

Prop name casing

Always use camelCase for prop declarations and within templates

const props = defineProps({
    greetingText: String,
});
<WelcomeMessage greetingText="hi" />

Multi-attribute elements

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" />

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((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(' ')
)

Simple computed properties

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);

Quoted attribute values

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>

Directive shorthands

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 component top-level element order

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>