# 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á:
- Treinar seu cérebro para analisar mais facilmente a maior parte do código da comunidade que encontrar.
- Ser capaz de copiar e colar a maior parte dos exemplos de código da comunidade sem modificações.
- 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
# 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', {
// ...
})
2
3
export default {
name: 'Todo',
// ...
}
2
3
4
Bom
app.component('todo-item', {
// ...
})
2
3
export default {
name: 'TodoItem',
// ...
}
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']
2
Bom
props: {
status: String
}
2
3
// Ainda melhor!
props: {
status: {
type: String,
required: true,
validator: value => {
return [
'syncing',
'synced',
'version-conflict',
'error'
].includes(value)
}
}
}
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'
}
]
}
}
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>
2
3
4
5
Bom
<ul>
<li
v-for="todo in todos"
:key="todo.id"
>
{{ todo.text }}
</li>
</ul>
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, substituausers
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 ov-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>
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)
}
}
2
3
4
5
<ul>
<li
v-for="user in activeUsers"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
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>
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>
2
3
4
5
6
7
8
9
Bom
<ul>
<li
v-for="user in activeUsers"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
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>
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>
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>
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>
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>
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() {
// ...
}
}
}
2
3
4
5
6
7
8
const myGreatMixin = {
// ...
methods: {
_update() {
// ...
}
}
}
2
3
4
5
6
7
8
const myGreatMixin = {
// ...
methods: {
$update() {
// ...
}
}
}
2
3
4
5
6
7
8
const myGreatMixin = {
// ...
methods: {
$_update() {
// ...
}
}
}
2
3
4
5
6
7
8
Bom
const myGreatMixin = {
// ...
methods: {
$_myGreatMixin_update() {
// ...
}
}
}
2
3
4
5
6
7
8
// Ainda melhor!
const myGreatMixin = {
// ...
methods: {
publicMethod() {
// ...
myPrivateFunction()
}
}
}
function myPrivateFunction() {
// ...
}
export default myGreatMixin
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Regras Prioridade B: Fortemente Recomendadas
# 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', {
// ...
})
2
3
4
5
6
7
Bom
components/
|- TodoList.js
|- TodoItem.js
2
3
components/
|- TodoList.vue
|- TodoItem.vue
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
2
components/
|- myComponent.vue
2
Bom
components/
|- MyComponent.vue
2
components/
|- my-component.vue
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
2
3
4
Bom
components/
|- BaseButton.vue
|- BaseTable.vue
|- BaseIcon.vue
2
3
4
components/
|- AppButton.vue
|- AppTable.vue
|- AppIcon.vue
2
3
4
components/
|- VButton.vue
|- VTable.vue
|- VIcon.vue
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
2
3
Bom
components/
|- TheHeading.vue
|- TheSidebar.vue
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
2
3
4
5
6
ou:
components/
|- TodoList/
|- Item/
|- Button.vue
|- Item.vue
|- TodoList.vue
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
2
3
4
components/
|- SearchSidebar.vue
|- NavigationForSearchSidebar.vue
2
3
Bom
components/
|- TodoList.vue
|- TodoListItem.vue
|- TodoListItemButton.vue
2
3
4
components/
|- SearchSidebar.vue
|- SearchSidebarNavigation.vue
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
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
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
2
3
4
5
6
7
Bom
components/
|- SearchButtonClear.vue
|- SearchButtonRun.vue
|- SearchInputQuery.vue
|- SearchInputExcludeGlob.vue
|- SettingsCheckboxTerms.vue
|- SettingsCheckboxLaunchOnStartup.vue
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>
2
<!-- Em templates DOM -->
<my-component/>
2
Bom
<!-- Em componentes single-file, templates string, e JSX -->
<MyComponent/>
2
<!-- Em templates DOM -->
<my-component></my-component>
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/>
2
<!-- Em componentes single-file e templates string -->
<myComponent/>
2
<!-- Em templates DOM -->
<MyComponent></MyComponent>
2
Bom
<!-- Em componentes single-file e templates string -->
<MyComponent/>
2
<!-- Em templates DOM -->
<my-component></my-component>
2
OU
<!-- Em qualquer lugar -->
<my-component></my-component>
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', {
// ...
})
2
3
import myComponent from './MyComponent.vue'
export default {
name: 'myComponent',
// ...
}
2
3
4
export default {
name: 'my-component',
// ...
}
2
3
4
Bom
app.component('MyComponent', {
// ...
})
2
3
app.component('my-component', {
// ...
})
2
3
import MyComponent from './MyComponent.vue'
export default {
name: 'MyComponent',
// ...
}
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
2
3
Bom
components/
|- StudentDashboardSettings.vue
|- UserProfileOptions.vue
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
}
2
3
<WelcomeMessage greetingText="hi"/>
Bom
props: {
greetingText: String
}
2
3
<WelcomeMessage greeting-text="hi"/>
# 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">
<MyComponent foo="a" bar="b" baz="c"/>
Bom
<img
src="https://vuejs.org/images/logo.png"
alt="Vue Logo"
>
2
3
4
<MyComponent
foo="a"
bar="b"
baz="c"
/>
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(' ')
}}
2
3
4
5
Bom
<!-- Em um template -->
{{ normalizedFullName }}
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(' ')
}
}
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)
)
}
}
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
}
}
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>
<AppSidebar :style={width:sidebarWidth+'px'}>
Bom
<input type="text">
<AppSidebar :style="{ width: sidebarWidth + 'px' }">
# 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"
>
2
3
4
<input
v-on:input="onInput"
@focus="onFocus"
>
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>
2
3
4
5
6
7
Bom
<input
:value="newTodoText"
:placeholder="newTodoInstructions"
>
2
3
4
<input
v-bind:value="newTodoText"
v-bind:placeholder="newTodoInstructions"
>
2
3
4
<input
@input="onInput"
@focus="onFocus"
>
2
3
4
<input
v-on:input="onInput"
v-on:focus="onFocus"
>
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>
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>
2
3
4
5
6
7
# Regras Prioridade C: Recomendadas
# 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.
Consciência Global (exige conhecimento além do componente)
name
Opções do Compilador de Template (mudam a maneira que templates são compilados)
compilerOptions
Dependências de Template (recursos usados no template)
components
directives
Composição (mescla propriedades nas opções)
extends
mixins
provide
/inject
Interface (a interface do componente)
inheritAttrs
props
emits
expose
API de Composição (ponto de entrada para usar a API de Composição)
setup
Estado Local (propriedades locais reativas)
data
computed
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
Propriedades não reativas (propriedades da instância independentes da reatividade do sistema)
methods
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.
Definição (fornece as opções do componente)
is
Renderização de Listas (cria múltiplas variações do mesmo elemento)
v-for
Condicionais (se o elemento é renderizado/mostrado)
v-if
v-else-if
v-else
v-show
v-cloak
Modificadores de Renderização (mudam a forma que o elemento é renderizado)
v-pre
v-once
Consciência Global (exige conhecimento além do componente)
id
Atributos Únicos (atributos que exigem valores únicos)
ref
key
Vinculação Bidirecional (combina eventos e vinculações)
v-model
Outros Atributos (todos os atributos não especificados vinculados ou não)
Eventos (escutadores de evento dos componentes)
v-on
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() {
// ...
}
}
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() {
// ...
}
}
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>
2
3
<!-- ComponentA.vue -->
<script>/* ... */</script>
<template>...</template>
<style>/* ... */</style>
<!-- ComponentB.vue -->
<template>...</template>
<script>/* ... */</script>
<style>/* ... */</style>
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>
2
3
4
5
6
7
8
9
<!-- ComponentA.vue -->
<template>...</template>
<script>/* ... */</script>
<style>/* ... */</style>
<!-- ComponentB.vue -->
<template>...</template>
<script>/* ... */</script>
<style>/* ... */</style>
2
3
4
5
6
7
8
9
# Regras Prioridade D: Use com Cautela
# 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>
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>
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">'
})
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>
`
})
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)"
>
`
})
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>
`
})
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)
}
}
})
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)
}
}
}
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>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24