Skip to content

Pravidla priority B: Silně doporučené

Tato pravidla byla zavedena pro zlepšení čitelnosti a/nebo komfortu pro vývojáře na většině projektů. Pokud je porušíte, váš kód bude stále fungovat, ale porušení by měla být vzácná a dobře odůvodněná.

Soubory komponent

Kdykoli je k dispozici build systém pro poskládání souborů, každá komponenta by měla být ve svém vlastním souboru.

To vám pomůže rychleji najít komponentu, když ji potřebujete upravit nebo zkontrolovat, jak ji používat.

Špatně

js
app.component('TodoList', {
  // ...
})

app.component('TodoItem', {
  // ...
})

Dobře

components/
|- TodoList.js
|- TodoItem.js
components/
|- TodoList.vue
|- TodoItem.vue

Velká a malá písmena v názvech Single-file komponent (SFC)

Názvy souborů Single-File komponent (SFC) by měly být buďto vždy PascalCase nebo vždy kebab-case.

PascalCase funguje nejlépe s automatickým dokončováním v editorech kódu, protože je konzistentní s tím, jak odkazujeme na komponenty v JS(X) a v šablonách, kdykoli je to možné. Názvy s mixem malých a velkých písmen však mohou někdy způsobit problémy v case-insensitive souborových systémech, a proto je kebab-case také dokonale přijatelný.

Špatně

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

Dobře

components/
|- MyComponent.vue
components/
|- my-component.vue

Názvy základních komponent

Základní komponenty (alias prezentační (presentational), hloupé (dumb) nebo čisté (pure) komponenty), ve kterých platí pravidla stylování a konvence typické pro aplikaci, by měly vždy začít se speciální předponou, například Base, App, or V.

Podrobné vysvětlení

Tyto komponenty pokládají základy pro konzistentní stylování a chování ve vaší aplikaci. Mohou obsahovat pouze:

  • HTML elementy,
  • jiné základní komponenty
  • UI komponenty třetích stran.

Ale nikdy neobsahují globální stav (např. state z Pinia store).

Jejich názvy často obsahují název prvku, který obalují (např. BaseButton, BaseTable), ledaže neexistuje žádný prvek pro jejich konkrétní účel (např. BaseIcon). Pokud vytváříte podobné komponenty pro specifičtější kontext, tyto komponenty je téměř vždy použijí (např. BaseButton může být použita v ButtonSubmit).

Některé výhody této konvence:

  • Když jsou v editoru uspořádány abecedně, základní součásti vaší aplikace jsou uvedeny společně, což usnadňuje jejich identifikaci.

  • Vzhledem k tomu, že názvy komponent by měly být vždy víceslovné, tato konvence vám brání volit libovolnou předponu pro jednoduché obaly komponent (např. MyButton, VueButton).

  • Vzhledem k tomu, že se tyto komponenty používají často, možná budete chtít, aby byly globální, místo abyste je všude importovali. Předpona to v kombinaci s Webpackem umožňuje:

    js
    const requireComponent = require.context(
      './src',
      true,
      /Base[A-Z]\w+\.(vue|js)$/
    )
    requireComponent.keys().forEach(function (fileName) {
      let baseComponentConfig = requireComponent(fileName)
      baseComponentConfig =
        baseComponentConfig.default || baseComponentConfig
      const baseComponentName =
        baseComponentConfig.name ||
        fileName.replace(/^.+\//, '').replace(/\.\w+$/, '')
      app.component(baseComponentName, baseComponentConfig)
    })

Špatně

components/
|- MyButton.vue
|- VueTable.vue
|- Icon.vue

Dobře

components/
|- BaseButton.vue
|- BaseTable.vue
|- BaseIcon.vue
components/
|- AppButton.vue
|- AppTable.vue
|- AppIcon.vue
components/
|- VButton.vue
|- VTable.vue
|- VIcon.vue

Těsně provázané názvy komponent

Komponenty potomků, ktero jsou těsně provázané s jejich rodičovskou komponentou, by měly obsahovat název nadřazené komponenty jako předponu.

Pokud má komponenta smysl pouze v kontextu jedné nadřazené komponenty, měl by být tento vztah zřejmý z jejího názvu. Vzhledem k tomu, že editory obvykle řadí soubory abecedně, toto také drží související soubory vedle sebe.

Podrobné vysvětlení

Možná budete v pokušení řešit tento problém vnořením podřízených komponent do adresářů pojmenovaných po jejich rodiči. Například:

components/
|- TodoList/
   |- Item/
      |- index.vue
      |- Button.vue
   |- index.vue

or:

components/
|- TodoList/
   |- Item/
      |- Button.vue
   |- Item.vue
|- TodoList.vue

To se nedoporučuje, protože to má za následek:

  • Mnoho souborů s podobnými názvy, což ztěžuje rychlé přepínání souborů v editorech kódu.
  • Mnoho vnořených podadresářů, což prodlužuje čas potřebný k procházení komponent v postranním panelu editoru.

Špatně

components/
|- TodoList.vue
|- TodoItem.vue
|- TodoButton.vue
components/
|- SearchSidebar.vue
|- NavigationForSearchSidebar.vue

Dobře

components/
|- TodoList.vue
|- TodoListItem.vue
|- TodoListItemButton.vue
components/
|- SearchSidebar.vue
|- SearchSidebarNavigation.vue

Pořadí slov v názvech komponent

Názvy komponent by měly začínat (většinou) nejobecnějšími slovy a končit popisnými modifikujícími výrazy.

Podrobné vysvětlení

Možná se ptáte:

""Proč bychom nutili názvy komponent používat méně přirozený jazyk?"

V přirozené angličtině se přídavná jména a další deskriptory obvykle objevují před podstatnými jmény, zatímco výjimky vyžadují spojovací slova. Například:

  • Coffee with milk
  • Soup of the day
  • Visitor to the museum

Pokud chcete, určitě můžete tato spojovací slova zahrnout do názvů komponent, ale pořadí je stále důležité.

Čeština

Výše uvedený text platí spíše pro anglický jazyk. V češtině sice také říkáme Káva s mlékem, ale vystačíme si s Polévka dne a Návštěvník muzea i bez spojovacích slov.

Také si uvědomte, že co je považováno za "nejvyšší level" bude platit v kontextu vaší aplikace. Představte si například aplikaci s vyhledávacím formulářem. Může obsahovat komponenty jako jsou tato:

components/
|- ClearSearchButton.vue
|- ExcludeFromSearchInput.vue
|- LaunchOnStartupCheckbox.vue
|- RunSearchButton.vue
|- SearchInput.vue
|- TermsCheckbox.vue

Jak jste si mohli všimnout, je docela obtížné zjistit, které komponenty jsou specifické pro vyhledávání. Nyní přejmenujme komponenty podle pravidla:

components/
|- SearchButtonClear.vue
|- SearchButtonRun.vue
|- SearchInputExcludeGlob.vue
|- SearchInputQuery.vue
|- SettingsCheckboxLaunchOnStartup.vue
|- SettingsCheckboxTerms.vue

Protože editory obvykle organizují soubory abecedně, všechny důležité vztahy mezi komponentami jsou nyní zřejmé na první pohled.

Možná vás napadlo vyřešit tento problém vnořením všech komponent vyhledávání do adresáře „search“ a všech komponent nastavení do adresáře „settings“. Tento přístup doporučujeme zvážit pouze ve skutečně velkých aplikacích (např. 100+ komponent), a to z těchto důvodů:

  • Procházení vnořenými podadresáři obvykle zabere více času než procházení jediným adresářem components.
  • Konflikty názvů (např. více komponent ButtonDelete.vue) ztěžují rychlou navigaci ke konkrétní komponentě v editoru kódu.
  • Refaktorování bude obtížnější, protože funkce find-and-replace často nestačí k aktualizaci relativních odkazů na přesouvanou komponentu. :::

Špatně

components/
|- ClearSearchButton.vue
|- ExcludeFromSearchInput.vue
|- LaunchOnStartupCheckbox.vue
|- RunSearchButton.vue
|- SearchInput.vue
|- TermsCheckbox.vue

Dobře

components/
|- SearchButtonClear.vue
|- SearchButtonRun.vue
|- SearchInputQuery.vue
|- SearchInputExcludeGlob.vue
|- SettingsCheckboxTerms.vue
|- SettingsCheckboxLaunchOnStartup.vue

Self-closing komponenty

Komponenty, kter nemají žádný obsah, by měly být zapsány jako nepárové (self-closing) v Single-File komponentách (SFC), string-šablonách, a JSX - nikdy však v in-DOM šablonách.

Komponenty zapsané jako nepárový (self-closing) tag sdělují nejen, že nemají žádný obsah, ale ani nemají žádný obsah mít. Je to rozdíl mezi prázdnou stránkou v knize a stránkou označenou „Tato stránka byla záměrně ponechána prázdná“. Váš kód je také bez zbytečného uzavíracího tagu čistší.

HTML bohužel neumožňuje uživatelské nepárové (self-closing) elementy – pouze oficiální „void“ elementy. To je důvod, proč je tato strategie možná pouze tehdy, když se Vue kompilátor šablon může k šabloně dostat před jejím vložením do DOM a poskytnout HTML vyhovující specifikaci.

Špatně

template
<!-- Single-File komponenty (SFC), string-šablony a JSX -->
<MyComponent></MyComponent>
template
<!-- in-DOM šablony -->
<my-component/>

Dobře

template
<!-- Single-File komponenty (SFC), string-šablony a JSX -->
<MyComponent/>
template
<!-- in-DOM šablony -->
<my-component></my-component>

Velká a malá písmena v názvech komponent v šablonách

Ve většině projektů názvy komponent v Single-File komponentách (SFC) a string-šablonách měly být vždy PascalCase – ale v in-DOM šablonách by názvy měly být kebab-case.

PascalCase má oproti kebab-case několik výhod:

  • Editory mohou v šablonách automaticky doplňovat názvy komponent, protože PascalCase se používá i v JavaScriptu.
  • <MyComponent> je vizuálně odlišnější od jednoslovného HTML elementu než <my-component>, protože existují dva rozdílné znaky (dvě velká písmena), nikoli pouze jeden (pomlčka).
  • Pokud ve svých šablonách použijete jakékoli custom prvky mimo Vue, jako je Web Component, PascalCase zajistí, že vaše Vue komponenty zůstanou zřetelně viditelné.

Bohužel kvůli necitlivosti HTML na malá a velká písmena musí in-DOM šablony nadále používat kebab-case.

Také vezměte do úvahy, že pokud jste již do kebab-case investovali hodně úsilí, může být konzistence s HTML konvencemi a možnost používat velká a malá písmena stejně ve všech vašich projektech důležitější než výše uvedené výhody. V těchto případech je také přijatelné používání kebab-case všude.

Špatně

template
<!-- Single-File komponenty (SFC) a string-šablony -->
<mycomponent/>
template
<!-- Single-File komponenty (SFC) a string-šablony -->
<myComponent/>
template
<!-- in-DOM šablony -->
<MyComponent></MyComponent>

Dobře

template
<!-- Single-File komponenty (SFC) a string-šablony -->
<MyComponent/>
template
<!-- in-DOM šablony -->
<my-component></my-component>

NEBO

template
<!-- Všude -->
<my-component></my-component>

Velká a malá písmena v názvech komponent v JS/JSX

Názvy komponent v JS/JSX by měly být vždy PascalCase, přestože mohou být kebab-case uvnitř řetězců pro jednodušší aplikace, které používají pouze globální registraci komponent skrz app.component.

Podrobné vysvětlení

V JavaScriptu je PascalCase konvencí pro třídy a prototype konstruktory - v podstatě pro cokoli, co může mít odlišné instance. Vue komponenty mají také instance, takže dává smysl rovněž používat PascalCase. Další výhodou je, že používání PascalCase v rámci JSX (a šablon) umožňuje čtenářům kódu snadněji rozlišovat mezi komponentami a HTML elementy.

Nicméně pro aplikace, které používají pouze globální definice komponent přes app.component, doporučujeme místo toho kebab-case. Důvody jsou:

  • Je vzácné, aby se v JavaScriptu někde odkazovalo na globální komponenty, takže dodržování konvence pro JavaScript nedává takový smysl.
  • Tyto aplikace vždy obsahují mnoho DOM-šabon, kde kebab-case musí být používán.

Špatně

js
app.component('myComponent', {
  // ...
})
js
import myComponent from './MyComponent.vue'
js
export default {
  name: 'myComponent'
  // ...
}
js
export default {
  name: 'my-component'
  // ...
}

Dobře

js
app.component('MyComponent', {
  // ...
})
js
app.component('my-component', {
  // ...
})
js
import MyComponent from './MyComponent.vue'
js
export default {
  name: 'MyComponent'
  // ...
}

Celá slova jako názvy komponent

Názvy komponent by měly upřednostňovat celá slova před zkratkami.

Automatické doplňování textu v editorech činí náklady na psaní delších názvů velmi nízké, přičemž srozumitelnost, kterou poskytují, je neocenitelná. Zejména méně obvyklým zkratkám je třeba se vyhnout vždy.

Špatně

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

Dobře

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

Velká a malá písmena v názvech vlastností (props)

Názvy vlastností (props) by měly při deklaraci vždy používat camelCase. Při použití uvnitř DOM-šablon by vlastnosti měly být psány kebab-case. Šablony Single-File komponent (SFC) a JSX mohou pro vlastnosti používat jak kebab-case, tak camelCase. Použití by mělo být konzistentní – pokud se rozhodnete použít rekvizity camelCased identifikátory, ujistěte se, že ve své aplikaci nepoužíváte i ty s kebab-case.

Špatně

js
props: {
  'greeting-text': String
}
js
const props = defineProps({
  'greeting-text': String
})
template
// DOM-šablony
<welcome-message greetingText="hi"></welcome-message>

Dobře

js
props: {
  greetingText: String
}
js
const props = defineProps({
  greetingText: String
})
template
// pro SFC - ujistěte se, že je použití malých a velkých písmen konzistentní napříč projektem
// můžete použít kteroukoli konvenci, ale nedoporučujeme míchat oba dva různé styly
<WelcomeMessage greeting-text="hi"/>
// nebo
<WelcomeMessage greetingText="hi"/>
template
// DOM-šablony
<welcome-message greeting-text="hi"></welcome-message>

Elementy s více atributy

Elementy s více atributy by měly měly být roztaženy na více řádků, s jedním atributem na řádek.

V JavaScriptu je dělení objektů s více vlastnostmi na více řádků obecně považováno za dobrou praxi, protože je mnohem snáz čitelné. Naše šablony a JSX si zaslouží stejnou úvahu.

Špatně

template
<img src="https://vuejs.org/images/logo.png" alt="Vue Logo">
template
<MyComponent foo="a" bar="b" baz="c"/>

Dobře

template
<img
  src="https://vuejs.org/images/logo.png"
  alt="Vue Logo"
>
template
<MyComponent
  foo="a"
  bar="b"
  baz="c"
/>

Jednoduché výrazy v šablonách

Šablony komponent by měly obsahovat pouze jednoduché výrazy a složitější výrazy přepracované do computed proměnných nebo funkcí.

Složité výrazy ve vašich šablonách je učiní méně deklarativní. Měli bychom se snažit popsat co by se mělo objevit, nikoli jak tuto hodnotu počítáme. Computed proměnné a funkce také umožňují opětovné použití kódu.

Špatně

template
{{
  fullName.split(' ').map((word) => {
    return word[0].toUpperCase() + word.slice(1)
  }).join(' ')
}}

Dobře

template
<!-- V šabloně -->
{{ normalizedFullName }}
js
// Složitý výraz byl přesunut do computed proměnné
computed: {
  normalizedFullName() {
    return this.fullName.split(' ')
      .map(word => word[0].toUpperCase() + word.slice(1))
      .join(' ')
  }
}
js
// Složitý výraz byl přesunut do computed proměnné
const normalizedFullName = computed(() =>
  fullName.value
    .split(' ')
    .map((word) => word[0].toUpperCase() + word.slice(1))
    .join(' ')
)

Jednoduché computed proměnné

Složité computed proměnné by měly být rozděleny na co nejvíce jednodušších proměnných.

Podrobné vysvětlení

Jednodušší, dobře pojmenované computed proměnné jsou:

  • Jednodušší na testování Když každá computed proměnná obsahuje pouze velmi jednoduchý výraz s velmi malým počtem závislostí, je mnohem jednodušší napsat testy potvrzující, že funguje správně.

  • Jednodušší pro čtení

    Zjednodušení computed proměnných vás nutí přiřadit každé hodnotě popisný název, i když není znovu použita. To usnadňuje ostatním vývojářům (a vašemu budoucímu já) soustředit se na kód, který je zajímá, a zjistit, o co v něm jde.

  • Lépe se přizpůsobují změnovým požadavkům

    Jakákoli hodnota, kterou lze pojmenovat, může být užitečná pro zobrazení. Můžeme se například rozhodnout zobrazit zprávu, která uživateli řekne, kolik peněz ušetřil. Můžeme se také rozhodnout vypočítat DPH, ale možná ji budeme chtít zobrazit samostatně, nikoli jako součást konečné ceny.

    Malé, cíleně vypočítané vlastnosti vytvářejí méně předpokladů o tom, jak budou informace použity, takže vyžadují méně refaktoringu, když se požadavky mění.

Špatně

js
computed: {
  price() {
    const basePrice = this.manufactureCost / (1 - this.profitMargin)
    return (
      basePrice -
      basePrice * (this.discountPercent || 0)
    )
  }
}
js
const price = computed(() => {
  const basePrice = manufactureCost.value / (1 - profitMargin.value)
  return basePrice - basePrice * (discountPercent.value || 0)
})

Dobře

js
computed: {
  basePrice() {
    return this.manufactureCost / (1 - this.profitMargin)
  },

  discount() {
    return this.basePrice * (this.discountPercent || 0)
  },

  finalPrice() {
    return this.basePrice - this.discount
  }
}
js
const basePrice = computed(
  () => manufactureCost.value / (1 - profitMargin.value)
)

const discount = computed(
  () => basePrice.value * (discountPercent.value || 0)
)

const finalPrice = computed(() => basePrice.value - discount.value)

Hodnoty atributů v úvozovkách

Neprázdné hodnoty HTML atributů by měly být vždy v uvozovkách (jednoduchých nebo dvojitých, podle toho, co se nepoužívá v JS).

Ačkoli hodnoty atributů bez mezer nemusí mít v HTML uvozovky, tato praxe často vede k vyhýbání se mezerám, takže hodnoty atributů jsou méně čitelné.

Špatně

template
<input type=text>
template
<AppSidebar :style={width:sidebarWidth+'px'}>

Dobře

template
<input type="text">
template
<AppSidebar :style="{ width: sidebarWidth + 'px' }">

Direktivní zkratky

Direktivní zkratky (: pro v-bind:, @ pro v-on: a # pro v-slot) by měly být použity buď všude nebo vůbec.

Špatně

template
<input
  v-bind:value="newTodoText"
  :placeholder="newTodoInstructions"
>
template
<input
  v-on:input="onInput"
  @focus="onFocus"
>
template
<template v-slot:header>
  <h1>Here might be a page title</h1>
</template>

<template #footer>
  <p>Here's some contact info</p>
</template>

Dobře

template
<input
  :value="newTodoText"
  :placeholder="newTodoInstructions"
>
template
<input
  v-bind:value="newTodoText"
  v-bind:placeholder="newTodoInstructions"
>
template
<input
  @input="onInput"
  @focus="onFocus"
>
template
<input
  v-on:input="onInput"
  v-on:focus="onFocus"
>
template
<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
<template #header>
  <h1>Here might be a page title</h1>
</template>

<template #footer>
  <p>Here's some contact info</p>
</template>
Pravidla priority B: Silně doporučené has loaded