Vue.js: Fundamentos de Componentes

Última atualização: 2026-08-26

Os componentes do Vue são instâncias reutilizáveis do Vue — cada componente encapsula seu próprio modelo, lógica e estilos. A arquitetura central do Vue 3 é a “árvore de componentes”: componente raiz (App.vue) → componentes de página → componentes de negócio → componentes básicos (Button/Input).

Dominar os conceitos básicos dos componentes é um passo crucial na criação de aplicações Vue em grande escala. Esta lição ajudará você a compreender a natureza dos componentes, como registrá-los, as convenções de nomenclatura e por que os SFCs (componentes de arquivo único) são a abordagem recomendada no Vue.

1. O que você vai aprender


2. Um backend de comércio eletrônico de 30 páginas afetado pela duplicação de código

(1) Desafio: 30 fichas de produto, 30 vezes que é preciso copiar e colar

A administradora do site de comércio eletrônico da Alice precisava exibir 30 fichas de produtos em várias páginas. Sua abordagem inicial:

HTML
<!-- ❌ The "Broken" Version: Copy Product Card 30 times -->
<!-- In HomeView.vue -->
<div class="product-card">
  <img src="iphone.jpg">
  <h3>iPhone 15 Pro</h3>
  <p>$999</p>
  <button>Add to Cart</button>
</div>
<div class="product-card">
  <img src="macbook.jpg">
  <h3>MacBook Pro</h3>
  <p>$2499</p>
  <button>Add to Cart</button>
</div>
<!-- ... And also 28 more ... -->

<!-- The same code appears again in ProductList.vue, SearchResults.vue, Cart.vue -->

O gerente de produto Charlie acrescenta 5 novos requisitos:

“Alice, preciso alterar o texto do botão de ‘Adicionar ao carrinho’ para ‘Adicionar à sacola’. Além disso, o cartão deve mostrar a disponibilidade do produto. E precisa ser responsivo em dispositivos móveis.”

Alice precisa editar mais de 30 arquivos HTML três vezes = 90 edições. Isso é impossível de manter.

(2) Solução com componente Vue: 1 arquivo ProductCard.vue, reutilizado 30 vezes

VUE
<!-- components/ProductCard.vue - 1 Component Definitions -->
<template>
  <div class="product-card">
    <img :src="product.image">
    <h3>{{ product.name }}</h3>
    <p>${{ product.price }}</p>
    <button :disabled="product.stock === 0" @click="addToCart">
      {{ product.stock === 0 ? 'Out of Stock' : 'Add to Bag' }}
    </button>
  </div>
</template>

<script setup>
const props = defineProps({ product: Object })
const emit = defineEmits(['add-to-cart'])

function addToCart() {
  emit('add-to-cart', props.product.id)
}
</script>
VUE
<!-- HomeView.vue / ProductList.vue / SearchResults.vue -->
<template>
  <ProductCard v-for="product in products" :key="product.id" :product="product" @add-to-cart="handleAdd" />
</template>

1 ProductCard.vue → reutilizado 30 vezes. Para alterar o texto do botão, basta fazer a alteração em um único lugar.

(3) Receita

Após a divisão em componentes:


3. O que é um componente?

(1) Definição

Um componente no Vue é uma unidade de interface do usuário reutilizável, com lógica e estilo próprios. Cada componente consiste em uma instância do Vue, além de seus próprios dados, métodos e ciclo de vida.

100%
graph TB
    A[App.vue<br/>Root Component] --> B[HomeView.vue<br/>Home]
    A --> C[AboutView.vue<br/>About]
    A --> D[DashboardView.vue<br/>Dashboard]
    
    B --> E[ProductCard.vue<br/>Product Card]
    B --> F[ProductList.vue<br/>Product List]
    B --> G[FilterBar.vue<br/>Filter Bar]
    
    E --> H[Button.vue<br/>Button]
    E --> I[Badge.vue<br/>Badge]
    
    F --> E
    G --> J[Select.vue<br/>Scroll down]
    G --> K[Input.vue<br/>Input]
    
    style A fill:#42b883,color:#fff
    style E fill:#42b883,color:#fff
    style H fill:#42b883,color:#fff

(2) 5 Principais Características

Recurso Descrição
Reutilizável 1 componente pode ser usado em N lugares
Encapsulamento Separar dados, métodos e estilo de forma que não interfiram uns nos outros
Modular Componentes menores combinados para formar componentes maiores
Testável Testes unitários de um único componente
Fácil de manter Alterar 1 lugar = Alterar N lugares

(3) Componentes x Funções

Dimensão Função Componente
Componentes reutilizáveis Lógica de código Interface do usuário + lógica
Entrada Argumentos (args) props
Saída retorno emissão
Estado Variáveis locais dados / referências
Efeitos colaterais Nenhum Ganchos de ciclo de vida

4. Componente de arquivo único (SFC)

(1) O que é o SFC?

SFC (Single File Component) = 1 arquivo .vue = 1 componente. Combinar as seções template, script e style em um único arquivo facilita o gerenciamento.

VUE
<!-- ProductCard.vue - SFC Single-File Components -->
<template>
  <!-- 1. template:HTML Template(Required) -->
  <div class="product-card">
    <h3>{{ product.name }}</h3>
    <p>${{ product.price }}</p>
  </div>
</template>

<script setup>
// 2. script:JS Logic(Required)
import { ref } from 'vue'

const props = defineProps({ product: Object })
const count = ref(0)
</script>

<style scoped>
/* 3. style:CSS Style(Optional) */
.product-card {
  border: 1px solid #e5e7eb;
  border-radius: 8px;
  padding: 1rem;
}
</style>

(2) Explicação detalhada da Parte 3 do SFC

Seção Função Obrigatória? Idioma
<template> Modelo HTML Comando HTML + Vue
<script setup> JS Logic JavaScript / TypeScript
<style scoped> Estilos CSS CSS / SCSS / Less

(3) SFC versus string HTML

JS
// ❌ No need SFC(String Templates,Vue 2 Style)
Vue.component('my-component', {
  template: '<div>{{ msg }}</div>',
  data() { return { msg: 'Hello' } }
})

// ✅ Use SFC (Recommended)
// MyComponent.vue
<template>
  <div>{{ msg }}</div>
</template>
<script setup>
const msg = 'Hello'
</script>

Vantagens do SFC:


5. Registro de componentes

(1) Registro global (não recomendado para projetos de grande porte)

JS
// main.js
import { createApp } from 'vue'
import App from './App.vue'
import ProductCard from './components/ProductCard.vue'

const app = createApp(App)

// Global Registration:All components are available <ProductCard />
app.component('ProductCard', ProductCard)

app.mount('#app')

(2) Registro local (recomendado)

VUE
<!-- HomeView.vue -->
<script setup>
// 1. Import
import ProductCard from '@/components/ProductCard.vue'

// 2. Use in a template(No registration required,Automatically Available)
</script>

<template>
  <ProductCard :product="product" />
</template>

(3) Comparação dos dois métodos de registro

Dimensão Registro global Registro local
Escopo Aplicação inteira Componente atual
Localização do código main.js Dentro do componente <script setup>
Tree-shaking ❌ Não compatível ✅ Compatível
Sugestões de tipo Fraca Forte (autocompletar do IDE)
Recomendação ⭐⭐ Para plugins ⭐⭐⭐⭐⭐ Para uso comercial

6. Convenções de nomenclatura de componentes

(1) 3 Convenções de nomenclatura

Estilo Exemplo Uso Recomendação
PascalCase ProductCard.vue Importação de JS / Nos modelos ⭐⭐⭐⭐⭐
kebab-case product-card.vue Nomes de arquivos / Modelos em kebab-case ⭐⭐⭐
camelCase productCard.vue Não recomendado

(2) Uso em modelos

VUE
<template>
  <!-- PascalCase(Recommendations,IDE Friendly) -->
  <ProductCard />
  <UserProfile />
  
  <!-- kebab-case(Also supported) -->
  <product-card />
  <user-profile />
  
  <!-- ✅ Either one works.,PascalCase Easier to read -->
</template>

(3) Estrutura de pastas

TEXT 📖 Somente leitura
src/
├── components/          # Public Components(For use across pages)
│   ├── ProductCard.vue
│   ├── Button.vue
│   └── Header.vue
├── views/               # Page Components(For routing)
│   ├── HomeView.vue
│   ├── ProductListView.vue
│   └── DashboardView.vue
├── composables/         # Composite Functions
│   ├── useAuth.ts
│   └── useFetch.ts
├── router/              # Router Configuration
└── App.vue              # Root Component

7. Noções básicas sobre props e emit

(1) props: Passadas do pai para o filho

VUE
<!-- Parent Component Parent.vue -->
<template>
  <ProductCard :product="myProduct" :show-stock="true" />
</template>

<!-- Child component ProductCard.vue -->
<template>
  <div>
    <h3>{{ product.name }}</h3>
    <p v-if="showStock">Stock: {{ product.stock }}</p>
  </div>
</template>

<script setup>
// Receive the data passed from the parent component props
const props = defineProps({
  product: { type: Object, required: true },
  showStock: { type: Boolean, default: false }
})
</script>

(2) emitir: do filho para o pai

VUE
<!-- Child component ProductCard.vue -->
<template>
  <button @click="handleAdd">Add to Cart</button>
</template>

<script setup>
const emit = defineEmits(['add-to-cart'])

function handleAdd() {
  emit('add-to-cart', { id: 1, name: 'iPhone' })
}
</script>
VUE
<!-- Parent Component Parent.vue -->
<template>
  <ProductCard @add-to-cart="handleAddToCart" />
</template>

<script setup>
function handleAddToCart(product) {
  console.log('Add to cart:', product)
}
</script>

(3) Referência rápida: props x emit

Categoria API Finalidade
Pai → Filho defineProps() Transferência de dados
Filho → Pai defineEmits() Notificação de evento

8. Exemplo completo: reutilização de 5 ProductCards

▶ Exemplo: 1. Componente de arquivo único ProductCard.vue

VUE 📖 Somente leitura
<!-- src/components/ProductCard.vue -->
<template>
  <div :class="['product-card', { 'out-of-stock': product.stock === 0 }]">
    <img :src="product.image" :alt="product.name">
    <h3>{{ product.name }}</h3>
    <p class="price">${{ product.price }}</p>
    <span class="stock" v-if="product.stock < 10 && product.stock > 0">
      Only {{ product.stock }} left
    </span>
    <button 
      :disabled="product.stock === 0" 
      @click="handleAddToCart"
    >
      {{ product.stock === 0 ? 'Out of Stock' : 'Add to Cart' }}
    </button>
  </div>
</template>

<script setup>
const props = defineProps({
  product: { 
    type: Object, 
    required: true,
    validator: (val) => val.id && val.name && val.price
  }
})

const emit = defineEmits(['add-to-cart'])

function handleAddToCart() {
  if (props.product.stock > 0) {
    emit('add-to-cart', props.product.id)
  }
}
</script>

<style scoped>
.product-card {
  border: 1px solid #e5e7eb;
  border-radius: 8px;
  padding: 1rem;
  transition: box-shadow 0.2s;
}
.product-card:hover {
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.out-of-stock {
  opacity: 0.6;
}
.price {
  color: #42b883;
  font-weight: bold;
}
button:disabled {
  background: #ccc;
  cursor: not-allowed;
}
</style>
53 linhas de lógica (limite de 40, somente leitura)

▶ Exemplo: 2. Reutilização do ProductCard em 3 páginas

VUE
<!-- src/views/HomeView.vue -->
<template>
  <h1>Featured Products</h1>
  <div class="grid">
    <ProductCard 
      v-for="product in featured" 
      :key="product.id" 
      :product="product"
      @add-to-cart="handleAdd"
    />
  </div>
</template>

<script setup>
import { ref } from 'vue'
import ProductCard from '@/components/ProductCard.vue'
import { useCart } from '@/composables/useCart'

const featured = ref([
  { id: 1, name: 'iPhone 15 Pro', price: 999, stock: 50, image: 'iphone.jpg' },
  { id: 2, name: 'MacBook Pro', price: 2499, stock: 20, image: 'macbook.jpg' }
])

const { addToCart } = useCart()
function handleAdd(productId) {
  addToCart(productId)
}
</script>
▶ Experimente
VUE
<!-- src/views/ProductListView.vue -->
<template>
  <ProductCard 
    v-for="product in products" 
    :key="product.id" 
    :product="product"
    @add-to-cart="handleAdd"
  />
</template>

<script setup>
import ProductCard from '@/components/ProductCard.vue'
// ... Exactly the same citation,Reusable Components
</script>

▶ Exemplo: 3. Comparação entre registro global e registro local

JS
// ❌ main.js Global Registration(Not recommended)
import ProductCard from './components/ProductCard.vue'
app.component('ProductCard', ProductCard)  // The entire app is available

// ✅ HomeView.vue Local Registration(Recommendations)
<script setup>
import ProductCard from '@/components/ProductCard.vue'
// Only at HomeView.vue Available in China
</script>
▶ Experimente

▶ Exemplo: 4. Os 5 principais tipos de adereços

VUE
<script setup>
defineProps({
  // Basic Types
  name: String,
  age: Number,
  active: Boolean,
  
  // Complex Types
  user: Object,
  items: Array,
  
  // Required + Default value
  title: { type: String, required: true },
  pageSize: { type: Number, default: 20 },
  
  // Validator
  email: { 
    type: String, 
    validator: (val) => val.includes('@') 
  },
  
  // Custom Types
  status: { 
    type: String, 
    validator: (val) => ['active', 'inactive'].includes(val) 
  }
})
</script>
▶ Experimente

▶ Exemplo: 5. 5 Princípios de design para componentes

Princípio Descrição
Responsabilidade Única Um componente faz uma coisa
Reutilizável Interface do usuário abstrata e genérica (botão/campo de entrada/janela modal)
Configurável as propriedades controlam o comportamento, os slots controlam o conteúdo
Modular Componentes menores combinados para formar componentes maiores
Fácil de testar Componentes independentes facilitam os testes unitários

▶ Exemplo: 6. 5 erros comuns

Erro Sintoma Solução
Arquivo da lista de componentes Erro do IDE Use PascalCase: ProductCard.vue
Número excessivo de registros globais Tamanho maior do pacote Os registros locais oferecem suporte ao “tree-shaking”
O componente é muito grande É difícil de manter Divida-o em componentes menores
Modificação direta de props Aviso do Vue Os props são somente para leitura; use emit em vez disso
Esqueci de importar um componente O modelo exibe “componente desconhecido” Verifique a importação em <script setup>

❓ Perguntas Frequentes

P: Quando se deve usar o registro global? R: Apenas em duas situações: (1) Componentes plug-in (como o <el-button> da Element Plus); (2) Componentes básicos utilizados em toda a aplicação (botão/ícone). Os componentes de negócios devem ser sempre registrados localmente para que o tree-shaking possa surtir efeito.

P: O SFC precisa ter todas as três partes? R: <template> e <script> são obrigatórios; <style> é opcional. Se um componente consistir apenas em JavaScript (por exemplo, se for apenas para encapsulamento de dados), é possível omitir o modelo (usando a função render).

P: Qual é o tamanho adequado para um arquivo de componente? R: Recomendamos que ele tenha menos de 200 linhas. Se ultrapassar 300 linhas, considere dividi-lo. Quanto mais simples for o componente, mais fácil será mantê-lo.

P: O SFC usa a extensão .vue. Como outras ferramentas a reconhecem? R: O Vite, o Webpack e o Vue CLI reconhecem a extensão .vue por padrão. Para o VS Code, instale a extensão “Vue - Official”.

P: Como faço para testar um único componente? R: Use o Vitest + Vue Test Utils (@vue/test-utils): mount(ProductCard, { props: { product: mockProduct } }). A Fase 4.6 deste tutorial aborda esse assunto em detalhes.

P: Como faço para usar a biblioteca de componentes (Element Plus)? R: Instale via npm → Registre globalmente no main.js → Use <el-button> diretamente em seus modelos. Para mais detalhes, consulte o Capítulo 4.7 do Phase.

P: Qual é a diferença entre SFC e React JSX? R: O SFC usa modelos semelhantes ao HTML (próximos aos padrões da web), enquanto o React usa JSX (escrita de HTML com JavaScript). O SFC tem uma curva de aprendizado suave, enquanto o JSX é flexível, mas requer uma etapa de compilação. O Vue 3 também oferece suporte ao JSX (@vitejs/plugin-vue-jsx), mas o SFC é a recomendação padrão.


📖 Resumo


📝 Exercícios

  1. Questões básicas (Dificuldade: ⭐)

    Crie um componente Button src/components/BaseButton.vue:

    • props: text (String), variant (String, 'primário'/'sucesso'/'perigo')
    • As 3 variantes correspondem a 3 classes
    • emitir o evento click
    • Use três variantes diferentes no arquivo HomeView.vue
  2. Problemas avançados (Dificuldade: ⭐⭐)

    Criar uma lista de produtos + sistema de cartões:

    • ProductCard.vue: Cartão de produto único (props: product, emit: add-to-cart)
    • ProductList.vue: Contêiner da lista de produtos (v-for + filtragem)
    • App.vue: Componente raiz; transmite dados e lida com o evento “adicionar ao carrinho”
    • 3 arquivos e 3 componentes utilizados em uma estrutura aninhada
  3. Problema desafiador (Dificuldade: ⭐⭐⭐)

    Implementar um sistema completo de componentes de “ficha de usuário”:

    1. Avatar.vue: Componente de avatar (compatível com os modos de URL e iniciais)
    2. UserCard.vue: Cartão do usuário (com avatar)
    3. UserList.vue: Lista de usuários (utilizando UserCard)
    4. App.vue: Componente raiz (Dados + Interação)
    5. 5 atributos (avatar/nome/e-mail/função/ativo)
    6. 3 eventos emitidos (editar/excluir/selecionar)
    7. Isolamento de estilos em escopo
Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%