# Guia de Estilos

Este é o guia oficial de estilos para código em Vue. Se você usa Vue em um projeto, esta é uma ótima referência para evitar erros, perda de tempo com trivialidades e antipadrões. Entretanto, não acreditamos que exista um guia de estilo ideal para todos os times ou projetos, então variações conscientes são incentivadas com base em suas experiências passadas, tecnologias do ambiente e valores pessoais.

Para a maior parte, evitamos também sugestões sobre JavaScript ou HTML em geral. Não nos importamos se você usa ponto e vírgula ou vírgulas à direita (trailing). Nem se seu HTML usa aspas simples ou duplas para valores de atributos. Entretanto, algumas exceções podem existir onde achamos que um padrão em particular seria útil no contexto do Vue.

Finalmente, separamos as regras em quatro categorias:

# Categorias de Regras

# Prioridade A: Essencial

Estas regras ajudam a prevenir erros, então aprenda e respeite-as a todo custo. Exceções podem existir, mas devem ser muito raras e feitas somente por aqueles com conhecimento avançado em JavaScript e Vue.

# Prioridade B: Fortemente Recomendado

Descobriu-se que essas regras melhoram a legibilidade e/ou a experiência do desenvolvedor na maioria dos projetos. Seu código ainda funcionará se você violá-las, mas as violações devem ser raras e bem justificadas.

# Prioridade C: Recomendado

Onde múltiplas opções igualmente boas existem, uma escolha arbitrária pode ser feita para garantir consistência. Nestas regras, descrevemos cada opção aceitável e sugerimos uma escolha padrão. Isso significa que você pode ficar à vontade para realizar uma escolha diferente em seu próprio código, desde que você seja consistente e tenha um bom motivo. Mas por favor, tenha um bom motivo! Ao adotar o padrão da comunidade, você irá:

  1. Treinar seu cérebro para analisar mais facilmente a maior parte do código da comunidade que encontrar.
  2. Ser capaz de copiar e colar a maior parte dos exemplos de código da comunidade sem modificações.
  3. Frequentemente encontrar novos parceiros que já estão acostumados ao seu estilo de código preferido, ao menos no âmbito Vue.

# Prioridade D: Use Cautelosamente

Alguns recursos do Vue existem para lidar com casos extremos ou migrações mais suaves de um código legado. Entretanto, quando usados em excesso, podem tornar o seu código mais difícil de manter ou até mesmo torná-lo um reduto de bugs. Estas regras ilustram recursos potencialmente arriscados, descrevendo quando e porque eles devem ser evitados.

# Regras Prioridade A: Essencial (Prevenção de Erros)

# Nomes de componente multipalavras essencial

Nomes de componente sempre devem ser multipalavras, exceto para componentes raiz App, e componentes internos fornecidos pelo Vue, como <transition>, ou <component>.

Isto previne conflitos (opens new window) com elementos HTML existentes e futuros, visto que todos os elementos HTML são formados por apenas uma palavra.

Ruim

app.component('todo', {
  // ...
})
1
2
3
export default {
  name: 'Todo',
  // ...
}
1
2
3
4

Bom

app.component('todo-item', {
  // ...
})
1
2
3
export default {
  name: 'TodoItem',
  // ...
}
1
2
3
4

# Definições de propriedades essencial

Definições de propriedades devem ser as mais detalhadas possíveis.

No código desenvolvido, definições de propriedades sempre devem ser tão detalhadas quanto possível, especificando ao menos os seus tipos.

Explicação Detalhada

Definições de propriedades detalhadas possuem duas vantagens:

  • Elas documentam a API do componente, para que seja fácil ver como o componente deve ser usado.
  • No desenvolvimento, o Vue irá lhe avisar se um componente receber propriedades formatadas incorretamente, ajudando-o a capturar potenciais causas de erro.

Ruim

// Isto é aceitável apenas ao prototipar
props: ['status']
1
2

Bom

props: {
  status: String
}
1
2
3
// Ainda melhor!
props: {
  status: {
    type: String,
    required: true,

    validator: value => {
      return [
        'syncing',
        'synced',
        'version-conflict',
        'error'
      ].includes(value)
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# Chaves de identificação no v-for essencial

Sempre use key com v-for.

key com v-for é sempre exigido em componentes, de forma a manter o estado do componente interno até a subárvore. E mesmo para elementos, é uma boa prática manter o comportamento previsível, como a constância de objetos (inglês) (opens new window) em animações.

Explicação Detalhada

Digamos que você tenha uma lista de tarefas:

data() {
  return {
    todos: [
      {
        id: 1,
        text: 'Aprender a usar v-for'
      },
      {
        id: 2,
        text: 'Aprender a usar key'
      }
    ]
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

E então você as ordena alfabeticamente. Ao atualizar o DOM, o Vue irá aperfeiçoar a renderização para desempenhar o menor número possível de mutações no DOM. Isto pode significar apagar o primeiro elemento de tarefa, e então adicioná-lo novamente ao final da lista.

O problema é que há casos em que é importante não apagar elementos que permanecerão no DOM. Por exemplo, você pode querer usar <transition-group> para animar a ordenação da lista, ou manter o foco se o elemento renderizado é um <input>. Nestes casos, adicionar uma chave única para cada item (ex.: :key="todo.id") irá dizer ao Vue como se comportar de forma mais previsível.

Em nossa experiência, o melhor é sempre adicionar uma chave única, para que você e seu time nunca precisem se preocupar com esses casos extremos. Então, em raros cenários com questões críticas de desempenho onde a constância de objetos não é necessária, você pode fazer uma exceção de forma consciente.

Ruim

<ul>
  <li v-for="todo in todos">
    {{ todo.text }}
  </li>
</ul>
1
2
3
4
5

Bom

<ul>
  <li
    v-for="todo in todos"
    :key="todo.id"
  >
    {{ todo.text }}
  </li>
</ul>
1
2
3
4
5
6
7
8

# Evitar v-if com v-for essencial

Nunca use v-if com v-for no mesmo elemento.

Geralmente existem dois casos onde isso pode ser tentador:

  • Para filtrar itens em uma lista (ex.: v-for="user in users" v-if="user.isActive"). Nestes casos, substitua users com um novo dado computado que retorne a sua lista filtrada (ex.: activeUsers).

  • Para evitar renderizar uma lista se ela deverá ser escondida (ex.: v-for="user in users" v-if="shouldShowUsers"). Nestes casos, mova o v-if para o elemento pai. (ex.: ul, ol).

Explicação Detalhada

Quando o Vue processa diretrizes, o v-if tem uma prioridade maior do que o v-for, então para este template:

<ul>
  <li
    v-for="user in users"
    v-if="user.isActive"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>
1
2
3
4
5
6
7
8
9

Acontecerá um erro, porque a diretriz v-if será avaliada primeiro e a variável de iteração user não existe neste momento.

Isto pode ser arrumado ao iterar sobre um dado computado, assim:

computed: {
  activeUsers() {
    return this.users.filter(user => user.isActive)
  }
}
1
2
3
4
5
<ul>
  <li
    v-for="user in activeUsers"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>
1
2
3
4
5
6
7
8

Alternativamente, podemos usar uma tag <template> com v-for para envolver o elemento <li>:

<ul>
  <template v-for="user in users" :key="user.id">
    <li v-if="user.isActive">
      {{ user.name }}
    </li>
  </template>
</ul>
1
2
3
4
5
6
7

Ruim

<ul>
  <li
    v-for="user in users"
    v-if="user.isActive"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>
1
2
3
4
5
6
7
8
9

Bom

<ul>
  <li
    v-for="user in activeUsers"
    :key="user.id"
  >
    {{ user.name }}
  </li>
</ul>
1
2
3
4
5
6
7
8
<ul>
  <template v-for="user in users" :key="user.id">
    <li v-if="user.isActive">
      {{ user.name }}
    </li>
  </template>
</ul>
1
2
3
4
5
6
7

# Escopo de estilos do componente essencial

Para aplicativos, estilos em um componente App de nível superior e em componentes de layout podem ser globais, mas todos os outros componentes devem ter seu próprio escopo de estilos.

Isto é relevante apenas para componentes single-file. Não é exigido que o atributo scoped (opens new window) seja usado. O escopo pode ser realizado através de módulos CSS (opens new window), uma estratégia baseada em classes como BEM (opens new window), ou outra biblioteca/convenção.

Bibliotecas de componentes, entretanto, devem preferir uma estratégia baseada em classes ao invés de usar o atributo scoped.

Isto torna mais fácil a sobreposição de estilos internos, com nomes de classes de fácil leitura que não possuem especificidade muito alta, e que são muito improváveis de resultarem em conflito.

Explicação Detalhada

Se você está desenvolvendo um projeto grande, trabalhando com outros desenvolvedores, ou às vezes incluindo algum HTML/CSS de terceiros (ex.: do Auth0), um escopo consistente irá garantir que seus estilos se apliquem somente aos componentes para que foram designados.

Além do atributo scoped, usar nomes de classe únicos podem ajudar a garantir que o CSS de terceiros não se apliquem em seu próprio HTML. Por exemplo, muitos projetos usam nomes de classe button, btn, ou icon, então mesmo ao não usar uma estratégia como o BEM, adicionar um prefixo específico do app e/ou específico do componente (ex.: ButtonClose-Icon) pode fornecer alguma proteção.

Ruim

<template>
  <button class="btn btn-close">×</button>
</template>

<style>
.btn-close {
  background-color: red;
}
</style>
1
2
3
4
5
6
7
8
9

Bom

<template>
  <button class="button button-close">×</button>
</template>

<!-- Usando o atributo `scoped` -->
<style scoped>
.button {
  border: none;
  border-radius: 2px;
}

.button-close {
  background-color: red;
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template>
  <button :class="[$style.button, $style.buttonClose]">×</button>
</template>

<!-- Usando módulos CSS -->
<style module>
.button {
  border: none;
  border-radius: 2px;
}

.buttonClose {
  background-color: red;
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template>
  <button class="c-Button c-Button--close">×</button>
</template>

<!-- Usando a convenção BEM -->
<style>
.c-Button {
  border: none;
  border-radius: 2px;
}

.c-Button--close {
  background-color: red;
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# Nomes de propriedades privadas essencial

Use o escopo do módulo para manter funções privadas inacessíveis pelo exterior. Se isto não for possível, sempre use o prefixo $_ para propriedades privadas customizadas em um plugin, mixin, etc, que não deva ser considerado como API pública. Então para evitar conflitos com o código de outros autores, também inclua um nome de escopo (ex.: $_nomeDoSeuPlugin_).

Explicação Detalhada

Vue usa o prefixo _ para definir suas próprias propriedades privadas, então usar o mesmo prefixo (ex.: _update) pode sobrescrever uma propriedade da instância. Mesmo que você tenha conferido e o Vue não esteja atualmente usando um nome de propriedade privada, não há garantias de que um conflito não acontecerá em uma versão posterior.

Quanto ao prefixo $, seu propósito dentro do ecossistema Vue são propriedades de instâncias especiais que são expostas ao usuário, então usá-las em propriedades privadas não seria apropriado.

Ao invés disso, recomendamos combinar os dois prefixos no $_, como convenção para propriedades privadas definidas pelo usuário que garantem que não haja conflitos com o Vue.

Ruim

const myGreatMixin = {
  // ...
  methods: {
    update() {
      // ...
    }
  }
}
1
2
3
4
5
6
7
8
const myGreatMixin = {
  // ...
  methods: {
    _update() {
      // ...
    }
  }
}
1
2
3
4
5
6
7
8
const myGreatMixin = {
  // ...
  methods: {
    $update() {
      // ...
    }
  }
}
1
2
3
4
5
6
7
8
const myGreatMixin = {
  // ...
  methods: {
    $_update() {
      // ...
    }
  }
}
1
2
3
4
5
6
7
8

Bom

const myGreatMixin = {
  // ...
  methods: {
    $_myGreatMixin_update() {
      // ...
    }
  }
}
1
2
3
4
5
6
7
8
// Ainda melhor!
const myGreatMixin = {
  // ...
  methods: {
    publicMethod() {
      // ...
      myPrivateFunction()
    }
  }
}

function myPrivateFunction() {
  // ...
}

export default myGreatMixin
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# Regras Prioridade B: Fortemente Recomendadas (Aprimoram Legibilidade)

# Arquivos de componente fortemente recomendado

Sempre que um sistema de compilação estiver disponível para concatenar arquivos, cada componente deve estar em seu próprio arquivo.

Isto ajuda você a encontrar mais rapidamente um componente quando precisar editá-lo ou verificar como usá-lo.

Ruim

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

app.component('TodoItem', {
  // ...
})
1
2
3
4
5
6
7

Bom

components/
|- TodoList.js
|- TodoItem.js
1
2
3
components/
|- TodoList.vue
|- TodoItem.vue
1
2
3

# Notação de nomes de Componentes Single-File fortemente recomendado

Nomes de arquivo de componentes single-file devem ser sempre PascalCase ou kebab-case.

PascalCase funciona melhor com o autocompletar em editores de código onde possível, pois é consistente com a forma que referenciamos componentes em JS(X) e templates. Entretanto, diferentes tipos de nomes de arquivo podem às vezes causar problemas em sistemas de arquivos insensíveis a maiúsculas e minúsculas, é a razão de o kebab-case ser perfeitamente aceitável.

Ruim

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

Bom

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

# Nomes de componentes base fortemente recomendado

Componentes base (ex.: de apresentação, sem lógica ou puros) que aplicam estilos e convenções específicos do aplicativo devem começar com um prefixo específico, como Base, App ou V.

Explicação Detalhada

Estes componentes estabelecem a base para o estilo e o comportamento consistente na sua aplicação. Eles podem conter somente:

  • Elementos HTML,
  • outros componentes base, e
  • componentes UI de terceiros.

Mas eles nunca irão conter estado global (ex.: de um store Vuex).

Seus nomes frequentemente incluem o nome do elemento que eles envolvem (ex.: BaseButton, BaseTable), a não ser que nenhum elemento exista para seu propósito específico (ex.: BaseIcon). Se você construir componentes similares para um contexto mais específico, eles quase sempre consumirão estes componentes (ex.: BaseButton será usado em ButtonSubmit).

Algumas vantagens desta convenção:

  • Quando organizado alfabeticamente em editores, os componentes base da aplicação serão listados em conjunto, tornando-os mais fáceis de identificar.

  • Como nomes de componente sempre devems ser multipalavras, esta convenção previne que você tenha que escolher um prefixo arbitrário para simples componentes wrapper (ex.: MyButton, VueButton).

  • Como estes componentes são frequentemente usados, você pode simplesmente torná-los globais ao invés de importá-los em todos os lugares. Um prefixo torna isto possível com o Webpack:

    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)
    })
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

Ruim

components/
|- MyButton.vue
|- VueTable.vue
|- Icon.vue
1
2
3
4

Bom

components/
|- BaseButton.vue
|- BaseTable.vue
|- BaseIcon.vue
1
2
3
4
components/
|- AppButton.vue
|- AppTable.vue
|- AppIcon.vue
1
2
3
4
components/
|- VButton.vue
|- VTable.vue
|- VIcon.vue
1
2
3
4

# Nomes de componentes de instância única fortemente recomendado

Componentes que devem ter somente uma única instância ativa devem começar com o prefixo The, para denotar que poderá existir somente um.

Isto não significa que o componente é usado apenas em uma única página, mas que será usado uma vez por página. Estes componentes nunca aceitam quaisquer propriedades, pois são específicos à sua aplicação, e não ao contexto dentro da sua aplicação. Se você encontrar a necessidade de adicionar propriedades, é uma boa indicação de que este na verdade é um componente reutilizável que é usado uma vez por página por enquanto.

Ruim

components/
|- Heading.vue
|- MySidebar.vue
1
2
3

Bom

components/
|- TheHeading.vue
|- TheSidebar.vue
1
2
3

# Nomes de componentes estreitamente acoplados fortemente recomendado

Componentes filho que são estreitamente acoplados com seus pais devem incluir o nome do componente pai como prefixo.

Se um componente fizer sentido apenas no contexto de um único componente pai, este relacionamento deve ser evidente em seu nome. Como editores tipicamente organizam os arquivos alfabeticamente, isto irá ajudar a manter estes arquivos relacionados próximos uns dos outros.

Explicação Detalhada

Você pode ficar tentado a resolver este problema aninhando componentes filhos em diretórios nomeados com base em seu pai. Por exemplo:

components/
|- TodoList/
   |- Item/
      |- index.vue
      |- Button.vue
   |- index.vue
1
2
3
4
5
6

ou:

components/
|- TodoList/
   |- Item/
      |- Button.vue
   |- Item.vue
|- TodoList.vue
1
2
3
4
5
6

Isto não é recomendado, pois resulta em:

  • Muitos arquivos com nomes similares, fazendo com que trocas de arquivos rápidas em editores de código tornem-se mais difíceis.
  • Muitos subdiretórios aninhados, o que aumenta o tempo para procurar componentes na barra lateral do editor.

Ruim

components/
|- TodoList.vue
|- TodoItem.vue
|- TodoButton.vue
1
2
3
4
components/
|- SearchSidebar.vue
|- NavigationForSearchSidebar.vue
1
2
3

Bom

components/
|- TodoList.vue
|- TodoListItem.vue
|- TodoListItemButton.vue
1
2
3
4
components/
|- SearchSidebar.vue
|- SearchSidebarNavigation.vue
1
2
3

# Ordem das palavras em nomes de componente fortemente recomendado

Nomes de componentes devem começar com palavras de nível mais alto (muitas vezes a mais geral) e terminar com palavras modificadoras descritivas.

Explicação Detalhada

Você pode estar se perguntando:

"Por que forçamos nomes de componentes a usar uma linguagem menos natural?"

No inglês natural, adjetivos e outros descritores geralmente aparecem antes dos substantivos, enquanto exceções exigem palavras conectoras. Por exemplo:

  • Café com leite
  • Sopa do dia
  • Visitante do museu

Você definitivamente pode incluir estas palavras conectoras no nome dos componentes se quiser, mas a ordem ainda é importante.

Também note que o que é considerado "nível mais alto" será contextual à sua aplicação. Por exemplo, imagine uma aplicação com um formulário de busca. Ele pode incluir componentes como este:

components/
|- ClearSearchButton.vue
|- ExcludeFromSearchInput.vue
|- LaunchOnStartupCheckbox.vue
|- RunSearchButton.vue
|- SearchInput.vue
|- TermsCheckbox.vue
1
2
3
4
5
6
7

Como você pode perceber, é difícil ver quais componentes são específicos da busca. Agora vamos renomear os componentes de acordo com a regra:

components/
|- SearchButtonClear.vue
|- SearchButtonRun.vue
|- SearchInputExcludeGlob.vue
|- SearchInputQuery.vue
|- SettingsCheckboxLaunchOnStartup.vue
|- SettingsCheckboxTerms.vue
1
2
3
4
5
6
7

Como editores tipicamente organizam os arquivos alfabeticamente, todas as relações importantes entre componentes agora estão evidentes à vista.

Você pode ficar tentado a resolver este problema diferentemente, aninhando todos os componentes de busca em um diretório "search", e todos os componentes de configuração em um diretório "settings". Recomendamos considerar esta abordagem apenas em aplicações muito grandes (ex.: mais de 100 componentes), pelas seguintes razões:

  • Geralmente leva mais tempo navegar por subdiretórios aninhados, do que percorrer um único diretório components.
  • Conflitos com nomes (ex.: múltiplos componentes ButtonDelete.vue) tornam mais difícil navegar rapidamente para um componente específico no editor de código.
  • Refatorar torna-se mais difícil, já que buscar-e-substituir nem sempre será suficiente para alterar as referências relativas para um componente deslocado.

Ruim

components/
|- ClearSearchButton.vue
|- ExcludeFromSearchInput.vue
|- LaunchOnStartupCheckbox.vue
|- RunSearchButton.vue
|- SearchInput.vue
|- TermsCheckbox.vue
1
2
3
4
5
6
7

Bom

components/
|- SearchButtonClear.vue
|- SearchButtonRun.vue
|- SearchInputQuery.vue
|- SearchInputExcludeGlob.vue
|- SettingsCheckboxTerms.vue
|- SettingsCheckboxLaunchOnStartup.vue
1
2
3
4
5
6
7

# Componentes com autofechamento fortemente recomendado

Componentes sem conteúdo devem ser autofechados em componentes single-file, templates string, e JSX - mas nunca em templates DOM.

Componentes autofechados informam não apenas que não têm conteúdo, mas garantem que não devem ter conteúdo. É a diferença entre uma página em branco em um livro e uma rotulada "Esta página foi intencionalmente deixada em branco". Seu código também é mais limpo sem a tag de fechamento desnecessária.

Infelizmente, HTML não permite que elementos customizados tenham fechamento próprio - somente elementos "void" oficiais (opens new window). É por isso que a estratégia só é possível quando o compilador de template do Vue pode alcançar o template antes do DOM, e então servir o HTML conforme especificado ao DOM.

Ruim

<!-- Em componentes single-file, templates string, e JSX -->
<MyComponent></MyComponent>
1
2
<!-- Em templates DOM -->
<my-component/>
1
2

Bom

<!-- Em componentes single-file, templates string, e JSX -->
<MyComponent/>
1
2
<!-- Em templates DOM -->
<my-component></my-component>
1
2

# Notação de nomes de componente em templates fortemente recomendado

Na maioria dos projetos, os nomes de componente devem ser sempre PascalCase em componentes single-file e templates de string - e kebab-case em templates DOM.

PascalCase possui algumas vantagens sobre kebab-case:

  • Editores podem autocompletar nomes de componentes em templates, pois o PascalCase também é utilizado no JavaScript.
  • <MyComponent> é mais distintivo visualmente de um elemento HTML de palavra única do que <my-component>, pois há duas diferenças em caracteres (duas maiúsculas), ao invés de só uma (um hífen).
  • Se você usar qualquer elemento personalizado em seus templates que não do Vue, como um web component, PascalCase garante que seus componentes Vue permaneçam distintamente visíveis.

Infelizmente, devido à insensibilidade do HTML quanto a maiúsculas e minúsculas, templates DOM ainda precisam utilizar kebab-case.

Também note que se você já investiu bastante em kebab-case, a consistência com convenções HTML e ser capaz de usar o mesmo padrão pelos seus projetos pode ser mais importante do que as vantagens listadas acima. Nestes casos, utilizar kebab-case em todo lugar também é aceitável.

Ruim

<!-- Em componentes single-file e templates string -->
<mycomponent/>
1
2
<!-- Em componentes single-file e templates string -->
<myComponent/>
1
2
<!-- Em templates DOM -->
<MyComponent></MyComponent>
1
2

Bom

<!-- Em componentes single-file e templates string -->
<MyComponent/>
1
2
<!-- Em templates DOM -->
<my-component></my-component>
1
2

OU

<!-- Em qualquer lugar -->
<my-component></my-component>
1
2

# Notação de nomes de componentes em JS/JSX fortemente recomendado

Nomes de componentes em JS/JSX devem ser sempre PascalCase, apesar de que podem ser kebab-case dentro de strings para aplicações mais simples, que usam apenas registros globais de componentes através de app.component.

Explicação Detalhada

No JavaScript, PascalCase é a convenção para classes e construtores de prototype - essencialmente, qualquer coisa que possa ter instâncias diferentes. Componentes Vue também possuem instâncias, então faz sentido também usar PascalCase. Como um benefício extra, usar PascalCase com JSX (e templates) permite que os leitores do código consigam distinguir mais facilmente entre componentes e elementos HTML.

Entretanto, para aplicações que usam apenas definições globais de componente via app.component, recomendamos que kebab-case seja usado. Os motivos são:

  • É raro que componentes globais sejam referenciados no JavaScript, então seguir a convenção para o JavaScript faz menos sentido.
  • Essas aplicações sempre incluem muito templates dentro do DOM, onde kebab-case deve ser usado.

Ruim

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

Bom

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

# Palavras completas em nomes de componente fortemente recomendado

Nomes de componente devem preferir palavras completas ao invés de abreviações.

O preenchimento automático em editores torna o custo de escrever nomes maiores muito baixo, enquanto a clareza que eles fornecem é inestimável. Abreviações incomuns, em particular, devem sempre ser evitadas.

Ruim

components/
|- SdSettings.vue
|- UProfOpts.vue
1
2
3

Bom

components/
|- StudentDashboardSettings.vue
|- UserProfileOptions.vue
1
2
3

# Notação do nome de propriedades fortemente recomendado

Nomes de propriedades devem sempre usar camelCase em sua declaração, e kebab-case em templates e JSX.

Estamos simplesmente seguindo as convenções de cada linguagem. Dentro do JavaScript, camelCase é mais natural. Dentro do HTML é o kebab-case.

Ruim

props: {
  'greeting-text': String
}
1
2
3
<WelcomeMessage greetingText="hi"/>
1

Bom

props: {
  greetingText: String
}
1
2
3
<WelcomeMessage greeting-text="hi"/>
1

# Elementos com multiatributos fortemente recomendado

Elementos com vários atributos devem sempre ocupar múltiplas linhas, com um atributo por linha.

No JavaScript, dividir objetos com múltiplas propriedades por múltiplas linhas é considerada uma boa convenção, pois é muito mais fácil de se ler. Nossos templates e JSX merecem a mesma consideração.

Ruim

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

Bom

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

# Expressões simples em templates fortemente recomendado

Templates de componentes devem incluir apenas expressões simples, com expressões mais complexas sendo refatoradas em dados computados ou métodos.

Expressões complexas em seus templates os tornam menos declarativos. Devemos nos esforçar para descrever o quê deve aparecer, não como estamos computando aquele valor. Dados computados e métodos também permitem que o código seja reutilizado.

Ruim

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

Bom

<!-- Em um template -->
{{ normalizedFullName }}
1
2
// A expressão complexa foi movida para uma propriedade computada
computed: {
  normalizedFullName() {
    return this.fullName.split(' ')
      .map(word => word[0].toUpperCase() + word.slice(1))
      .join(' ')
  }
}
1
2
3
4
5
6
7
8

# Dados computados simples fortemente recomendado

Dados computados complexos devem ser divididos em quantos dados computados simples forem possíveis.

Explicação Detalhada

Dados computados mais simples e bem nomeados são:

  • Mais fáceis de testar

    Quando cada propriedade computada contém somente uma expressão muito simples, com poucas dependências, é muito mais fácil escrever testes confirmando que elas funcionam corretamente.

  • Mais fáceis de ler

    Simplificar dados computados força você a dar a cada valor um nome descritivo, mesmo que não seja reutilizado. Isso torna mais fácil para outros desenvolvedores (e você no futuro) a focarem no código que manipulam e no que está acontecendo.

  • Mais adaptáveis a mudanças de requisitos

    Qualquer valor que possa ser nomeado pode ser útil para a view. Por exemplo, podemos decidir mostrar a mensagem informando ao usuário quanto dinheiro ele economizou. Também podemos decidir como calcular as taxas das vendas, mas talvez mostrá-las separadamente, ao invés de como parte do preço final.

    Dados computados pequenos e focados trazem menos suposições sobre como a informação será usada, e exigem menos refatoração conforme os requisitos mudam.

Ruim

computed: {
  price() {
    const basePrice = this.manufactureCost / (1 - this.profitMargin)
    return (
      basePrice -
      basePrice * (this.discountPercent || 0)
    )
  }
}
1
2
3
4
5
6
7
8
9

Bom

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

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

  finalPrice() {
    return this.basePrice - this.discount
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# Aspas em valores de atributos fortemente recomendado

Valores de atributos HTML não vazios devem sempre estar dentro de aspas (simples ou duplas, a qual não for usada no JS).

Enquanto valores de atributo sem qualquer espaço não exigem aspas no HTML, esta prática frequentemente leva a evitar espaços, tornando os valores dos atributos menos legíveis.

Ruim

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

Bom

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

# Abreviação de diretivas fortemente recomendado

Abreviações de diretivas (: para v-bind:, @ para v-on: e # para v-slot) devem ser usadas sempre ou nunca.

Ruim

<input
  v-bind:value="newTodoText"
  :placeholder="newTodoInstructions"
>
1
2
3
4
<input
  v-on:input="onInput"
  @focus="onFocus"
>
1
2
3
4
<template v-slot:header>
  <h1>Aqui pode ser um título de página</h1>
</template>

<template #footer>
  <p>Aqui alguma informação de contato</p>
</template>
1
2
3
4
5
6
7

Bom

<input
  :value="newTodoText"
  :placeholder="newTodoInstructions"
>
1
2
3
4
<input
  v-bind:value="newTodoText"
  v-bind:placeholder="newTodoInstructions"
>
1
2
3
4
<input
  @input="onInput"
  @focus="onFocus"
>
1
2
3
4
<input
  v-on:input="onInput"
  v-on:focus="onFocus"
>
1
2
3
4
<template v-slot:header>
  <h1>Aqui pode ser um título de página</h1>
</template>

<template v-slot:footer>
  <p>Aqui alguma informação de contato</p>
</template>
1
2
3
4
5
6
7
<template #header>
  <h1>Aqui pode ser um título de página</h1>
</template>

<template #footer>
  <p>Aqui alguma informação de contato</p>
</template>
1
2
3
4
5
6
7

# Regras Prioridade C: Recomendadas (Minimizam Escolhas Arbitrárias e Sobrecarga Cognitiva)

# Ordem das opções de componente/instância recomendado

As opções de componente/instância devem ser sempre ordenadas consistentemente.

Esta é a ordem padrão que recomendamos para opções de componente. Elas são divididas em categorias, então você saberá onde adicionar novas propriedades de plugins.

  1. Consciência Global (exige conhecimento além do componente)

    • name
  2. Opções do Compilador de Template (mudam a maneira que templates são compilados)

    • compilerOptions
  3. Dependências de Template (recursos usados no template)

    • components
    • directives
  4. Composição (mescla propriedades nas opções)

    • extends
    • mixins
    • provide/inject
  5. Interface (a interface do componente)

    • inheritAttrs
    • props
    • emits
    • expose
  6. API de Composição (ponto de entrada para usar a API de Composição)

    • setup
  7. Estado Local (propriedades locais reativas)

    • data
    • computed
  8. Eventos (callbacks acionados por eventos reativos)

    • watch
    • Eventos do Ciclo de Vida (na ordem em que são chamados)
      • beforeCreate
      • created
      • beforeMount
      • mounted
      • beforeUpdate
      • updated
      • activated
      • deactivated
      • beforeUnmount
      • unmounted
      • errorCaptured
      • renderTracked
      • renderTriggered
  9. Propriedades não reativas (propriedades da instância independentes da reatividade do sistema)

    • methods
  10. Renderização (a descrição declarativa da saída do componente)

    • template/render

# Ordem dos atributos de elementos recomendado

Os atributos dos elementos (incluindo componentes) devem ser ordenados consistentemente.

Esta é a ordenação padrão que recomendamos para os atributos de componentes. Eles são divididos em categorias, então você saberá onde adicionar atributos e diretrizes personalizadas.

  1. Definição (fornece as opções do componente)

    • is
  2. Renderização de Listas (cria múltiplas variações do mesmo elemento)

    • v-for
  3. Condicionais (se o elemento é renderizado/mostrado)

    • v-if
    • v-else-if
    • v-else
    • v-show
    • v-cloak
  4. Modificadores de Renderização (mudam a forma que o elemento é renderizado)

    • v-pre
    • v-once
  5. Consciência Global (exige conhecimento além do componente)

    • id
  6. Atributos Únicos (atributos que exigem valores únicos)

    • ref
    • key
  7. Vinculação Bidirecional (combina eventos e vinculações)

    • v-model
  8. Outros Atributos (todos os atributos não especificados vinculados ou não)

  9. Eventos (escutadores de evento dos componentes)

    • v-on
  10. Conteúdo (sobrescrevem o conteúdo do elemento)

    • v-html
    • v-text

# Linhas vazias em opções de componente/instância recomendado

Você pode querer adicionar uma linha vazia entre propriedades de várias linhas, especialmente se as opções não couberem mais na sua tela sem rolagem.

Quando os componentes começam a parecer abarrotados ou difíceis de ler, adicionar espaços entre propriedades multilinhas pode torná-lo fácil para se ler novamente. Em alguns editores, como o Vim, opções de formatação como essa podem também fazer mais fácil de navegar com o teclado.

Bom

props: {
  value: {
    type: String,
    required: true
  },

  focused: {
    type: Boolean,
    default: false
  },

  label: String,
  icon: String
},

computed: {
  formattedValue() {
    // ...
  },

  inputClasses() {
    // ...
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Não ter espaços também é bom, desde que o componente
// seja fácil de ler e de navegar.
props: {
  value: {
    type: String,
    required: true
  },
  focused: {
    type: Boolean,
    default: false
  },
  label: String,
  icon: String
},
computed: {
  formattedValue() {
    // ...
  },
  inputClasses() {
    // ...
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# Ordem dos elementos de nível superior de componentes single-file recomendado

Componentes Single-File devem sempre ordenar as tags <script>, <template>, e <style> consistentemente, com a <style> por último, porque ao menos uma das outras duas é sempre necessária.

Ruim

<style>/* ... */</style>
<script>/* ... */</script>
<template>...</template>
1
2
3
<!-- ComponentA.vue -->
<script>/* ... */</script>
<template>...</template>
<style>/* ... */</style>

<!-- ComponentB.vue -->
<template>...</template>
<script>/* ... */</script>
<style>/* ... */</style>
1
2
3
4
5
6
7
8
9

Bom

<!-- ComponentA.vue -->
<script>/* ... */</script>
<template>...</template>
<style>/* ... */</style>

<!-- ComponentB.vue -->
<script>/* ... */</script>
<template>...</template>
<style>/* ... */</style>
1
2
3
4
5
6
7
8
9
<!-- ComponentA.vue -->
<template>...</template>
<script>/* ... */</script>
<style>/* ... */</style>

<!-- ComponentB.vue -->
<template>...</template>
<script>/* ... */</script>
<style>/* ... */</style>
1
2
3
4
5
6
7
8
9

# Regras Prioridade D: Use com Cautela (Padrões Potencialmente Perigosos)

# Seletores de elemento em scoped use com cautela

Seletores de elemento devem ser evitados com o scoped.

Prefira seletores de classe ao invés de seletores de elemento em estilos scoped, pois um grande número de seletores de elemento são lentos.

Explicação Detalhada

Para usar o escopo em estilos, o Vue adiciona um atributo único aos elementos de componente, como um data-v-f3f3eg9. Então os seletores são modificados para que apenas os elementos correspondentes com este atributo sejam selecionados. (ex.: button[data-v-f3f3eg9]).

O problema é que um grande número de seletores de elemento (ex.: button[data-v-f3f3eg9]) serão consideravelmente mais lentos do que seletores de classe (ex.: .btn-close[data-v-f3f3eg9]), então seletores de classe devem ser preferidos sempre que possível.

Ruim

<template>
  <button>×</button>
</template>

<style scoped>
button {
  background-color: red;
}
</style>
1
2
3
4
5
6
7
8
9

Bom

<template>
  <button class="btn btn-close">×</button>
</template>

<style scoped>
.btn-close {
  background-color: red;
}
</style>
1
2
3
4
5
6
7
8
9

# Comunicação implícita entre componentes pai-filho use com cautela

Propriedades e eventos devem ser preferidos para a comunicação entre componentes pai-filho, ao invés de this.$parent ou mutação de propriedades.

Uma aplicação Vue ideal passa propriedades para baixo, e eventos para cima. Ater-se a esta convenção irá tornar os seus componentes muito mais fáceis de entender. Entretanto, há casos extremos onde a mutação de propriedade ou o this.$parent poderá simplificar dois componentes que já estão profundamente atrelados.

O problema é que existem também muitos casos simples onde estes padrões podem oferecer conveniência. Cuidado: não seja seduzido a trocar simplicidade (ser capaz de entender o fluxo do seu estado) pela conveniência em curto prazo (escrever menos código).

Ruim

app.component('TodoItem', {
  props: {
    todo: {
      type: Object,
      required: true
    }
  },

  template: '<input v-model="todo.text">'
})
1
2
3
4
5
6
7
8
9
10
app.component('TodoItem', {
  props: {
    todo: {
      type: Object,
      required: true
    }
  },

  methods: {
    removeTodo() {
      this.$parent.todos = this.$parent.todos.filter(todo => todo.id !== vm.todo.id)
    }
  },

  template: `
    <span>
      {{ todo.text }}
      <button @click="removeTodo">
        ×
      </button>
    </span>
  `
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

Bom

app.component('TodoItem', {
  props: {
    todo: {
      type: Object,
      required: true
    }
  },

  emits: ['input'],

  template: `
    <input
      :value="todo.text"
      @input="$emit('input', $event.target.value)"
    >
  `
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
app.component('TodoItem', {
  props: {
    todo: {
      type: Object,
      required: true
    }
  },

  emits: ['delete'],

  template: `
    <span>
      {{ todo.text }}
      <button @click="$emit('delete')">
        ×
      </button>
    </span>
  `
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# Gerenciamento de estado sem Flux use com cautela

Vuex (opens new window) deve ser preferido para o gerenciamento de estado global, ao invés de this.$root ou um event bus global.

Gerenciar o estado em this.$root e/ou usando um event bus global pode ser conveniente para casos muito simples, mas é inapropriado para a maioria das aplicações.

Vuex é a implementação oficial no estilo Flux para o Vue, e oferece não apenas um local central para gerenciar o estado, mas também ferramentas para organizar, rastrear, e depurar alterações de estado. Ele integra bem o ecossistema Vue (incluindo o completo suporte a Vue DevTools).

Ruim

// main.js
import { createApp } from 'vue'
import mitt from 'mitt'
const app = createApp({
  data() {
    return {
      todos: [],
      emitter: mitt()
    }
  },

  created() {
    this.emitter.on('remove-todo', this.removeTodo)
  },

  methods: {
    removeTodo(todo) {
      const todoIdToRemove = todo.id
      this.todos = this.todos.filter(todo => todo.id !== todoIdToRemove)
    }
  }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

Bom

// store/modules/todos.js
export default {
  state: {
    list: []
  },

  mutations: {
    REMOVE_TODO (state, todoId) {
      state.list = state.list.filter(todo => todo.id !== todoId)
    }
  },

  actions: {
    removeTodo ({ commit, state }, todo) {
      commit('REMOVE_TODO', todo.id)
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!-- TodoItem.vue -->
<template>
  <span>
    {{ todo.text }}
    <button @click="removeTodo(todo)">
      X
    </button>
  </span>
</template>

<script>
import { mapActions } from 'vuex'

export default {
  props: {
    todo: {
      type: Object,
      required: true
    }
  },

  methods: mapActions(['removeTodo'])
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24