Vue.js: Renderização Condicional e Listas

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

A renderização condicional (v-if / v-show) e a renderização de lista (v-for) são as duas diretivas mais utilizadas nos modelos do Vue. A primeira controla se um elemento é exibido ou não, enquanto a segunda controla quantos elementos são exibidos. Juntas, elas são capazes de lidar com 90% dos cenários de interface do usuário.

Compreender as cinco principais diferenças entre v-if e v-for, bem como o uso correto de :key, é essencial para a criação de aplicativos Vue de alto desempenho.

1. O que você vai aprender

Renderização condicional


2. Um pesadelo de programação: “Instruções if aninhadas” em um painel

(1) Problema: 5 tags v-if aninhadas — um pesadelo para a depuração

O código do painel da Alice ficou fora de controle:

VUE
<!-- ❌ The "Broken" Version:v-if Nested Hell -->
<template>
 <div v-if="user">
 <div v-if="user.isAdmin">
 <div v-if="orders.length > 0">
 <div v-if="!isLoading">
 <div v-if="!hasError">
 <!-- Content is finally displayed -->
 <p>Orders: {{ orders.length }}</p>
 </div>
 <div v-else>Error: {{ errorMsg }}</div>
 </div>
 <div v-else>Loading...</div>
 </div>
 <div v-else>No orders</div>
 </div>
 <div v-else>Not admin</div>
 </div>
 <div v-else>Please login</div>
</template>

A revisão de código feita por Charlie:

“Alice, isso está impossível de ler. São cinco níveis de aninhamento de v-if. E se adicionarmos mais duas condições? Precisamos de um padrão mais claro.”

(2) Solução com Vue: Simplificação com v-if + Propriedades calculadas

VUE
<template>
 <div v-if="!user">Please login</div>
 
 <div v-else-if="!user.isAdmin">Not admin</div>
 
 <div v-else-if="isLoading">Loading...</div>
 
 <div v-else-if="hasError">Error: {{ errorMsg }}</div>
 
 <div v-else-if="orders.length === 0">No orders</div>
 
 <div v-else>
 <p>Orders: {{ orders.length }}</p>
 <OrderList :orders="orders" />
 </div>
</template>

<script setup>
import { computed } from 'vue'

const props = defineProps(['user', 'orders', 'isLoading', 'hasError', 'errorMsg'])

// Flatten 5 conditions (v-else-if chain)
</script>

(3) Receita


3. Renderização condicional com v-if / v-else-if / v-else

(1) Sintaxe básica

VUE
<template>
 <!-- Single condition -->
 <p v-if="isVisible">Visible</p>
 
 <!-- Choose one of the two -->
 <p v-if="score >= 60">Passed</p>
 <p v-else>Failed</p>
 
 <!-- Choose one or more -->
 <p v-if="type === 'A'">Type A</p>
 <p v-else-if="type === 'B'">Type B</p>
 <p v-else-if="type === 'C'">Type C</p>
 <p v-else>Unknown</p>
</template>

(2) O elemento template

VUE
<template>
 <!-- template:Grouping Multiple Elements,Do not render DOM -->
 <template v-if="isAdmin">
 <h1>Admin Dashboard</h1>
 <p>Welcome, {{ user.name }}</p>
 <button>Edit</button>
 </template>
 
 <!-- Does not render template element (Savings 1 DOM layer) -->
</template>

(3) 5 principais casos de uso

Cenário Usando v-if
Conectado/Não conectado
Carregando.../Carregado
Sem dados/Dados disponíveis
Status do erro
Alternar frequentemente entre mostrar e ocultar ❌ (Use v-show)

4. 5 principais diferenças entre v-show e v-if

(1) Principais diferenças

VUE
<template>
 <!-- v-if: When conditions are false, Elements removed from DOM -->
 <p v-if="show">v-if</p>
 <!-- show=false: The element does not exist in DOM -->
 
 <!-- v-show: When conditions are false, display: none -->
 <p v-show="show">v-show</p>
 <!-- show=false: <p style="display: none">v-show</p> -->
</template>

(2) 5 comparações importantes

Aspecto v-if v-show
Manipulação do DOM Remove o elemento quando a condição é falsa Permanece sempre no DOM; apenas a propriedade display é alterada
Desempenho da troca Ruim (destruir e reconstruir a cada vez) Bom (apenas CSS)
Renderização inicial Não renderizar se a condição for falsa Sempre renderizar (mesmo que seja invisível)
Compatibilidade com transições ✅ Perfeita (animações de entrada/saída) ❌ Não compatível
Casos de uso Troca ocasional Troca frequente

(3) Recomendações de seleção

JS
// Switch Occasionally(If you are logged in/Not logged in)→ v-if
<div v-if="isLoggedIn">Welcome, {{ user.name }}</div>
<div v-else>Please login</div>

// Frequent switching (e.g. tab switch) → v-show
<button v-show="isActive">Active</button>
<button v-show="!isActive">Inactive</button>

5. Exibição de listas com v-for

(1) Iterando por um array

VUE
<template>
 <!-- Basic Iteration -->
 <ul>
 <li v-for="item in items" :key="item.id">
 {{ item.name }}
 </li>
 </ul>
 
 <!-- Indexed -->
 <ul>
 <li v-for="(item, index) in items" :key="item.id">
 {{ index + 1 }}. {{ item.name }}
 </li>
 </ul>
</template>

<script setup>
import { ref } from 'vue'
const items = ref([
 { id: 1, name: 'Apple' },
 { id: 2, name: 'Banana' },
 { id: 3, name: 'Cherry' }
])
</script>

(2) Iteração sobre objetos

VUE
<template>
 <ul>
 <li v-for="(value, key, index) in user" :key="key">
 {{ index + 1 }}. {{ key }}: {{ value }}
 </li>
 </ul>
</template>

<script setup>
import { reactive } from 'vue'
const user = reactive({
 name: 'Alice',
 age: 25,
 email: 'alice@example.com'
})
// Output:1. name: Alice 2. age: 25 3. email: alice@example.com
</script>

(3) Iterando por números

VUE
<template>
 <!-- Rendering 1 to 10 -->
 <span v-for="n in 10" :key="n">{{ n }}</span>
 <!-- Output:12345678910 -->
</template>

(4) Iteração por uma string

VUE
<template>
 <!-- One character per line span -->
 <span v-for="char in 'Hello'" :key="char">{{ char }}</span>
 <!-- Output:H e l l o -->
</template>

6. A finalidade de :key e as melhores práticas

(1) O que é :key?

:key Atribua um identificador exclusivo a cada item do v-for para ajudar o Vue a atualizar o DOM de forma eficiente.

VUE
<template>
 <!-- ❌ None :key(Vue Use by default index) -->
 <li v-for="item in items">{{ item.name }}</li>
 
 <!-- ✅ Has :key (Use "unique" id) -->
 <li v-for="item in items" :key="item.id">{{ item.name }}</li>
</template>

(2) Por que o :key é necessário?

100%
graph LR
 A[Original List A B C] --> B[New List B C D]
 B --> C[None key:Possible misalignment]
 B --> D[Has key: Update Correctly]
 
 style C fill:#ff6b6b
 style D fill:#42b883

Quando não há :key:

Quando :key estiver presente:

(3) Melhores práticas para :key

VUE
<!-- ✅ Recommendations:The Only One ID -->
<li v-for="user in users" :key="user.id">

<!-- ✅ Second choice:Unique Field(Email、Mobile phone number, etc.)-->
<li v-for="user in users" :key="user.email">

<!-- ❌ Avoid:index(When the data changes, the alignment is off.)-->
<li v-for="(user, index) in users" :key="index">

<!-- ❌ Never, under any circumstances:random()(Every render is different,Forced Rebuild)-->
<li v-for="user in users" :key="Math.random()">

7. Filtragem / Ordenação / Paginação de listas

(1) Filtragem + Classificação (Propriedades calculadas)

JS
import { ref, computed } from 'vue'

const items = ref([
 { id: 1, name: 'iPhone', price: 999, category: 'phone' },
 { id: 2, name: 'MacBook', price: 2499, category: 'laptop' },
 { id: 3, name: 'iPad', price: 599, category: 'tablet' }
])

const searchQuery = ref('')
const selectedCategory = ref('all')
const sortBy = ref('price') // 'price' / 'name'

// Filter + Sort(computed Automatic Caching)
const filteredItems = computed(() => {
 let result = items.value
 
 // 1. Filter by search term
 if (searchQuery.value) {
 result = result.filter(item =>
 item.name.toLowerCase().includes(searchQuery.value.toLowerCase())
 )
 }
 
 // 2. Filter by Category
 if (selectedCategory.value !== 'all') {
 result = result.filter(item => item.category === selectedCategory.value)
 }
 
 // 3. Sort
 result = [...result].sort((a, b) => {
 if (sortBy.value === 'price') return a.price - b.price
 if (sortBy.value === 'name') return a.name.localeCompare(b.name)
 return 0
 })
 
 return result
})

(2) Paginação

JS
const currentPage = ref(1)
const pageSize = ref(10)

// Data after pagination
const paginatedItems = computed(() => {
 const start = (currentPage.value - 1) * pageSize.value
 return filteredItems.value.slice(start, start + pageSize.value)
})

// Total Number of Pages
const totalPages = computed(() =>
 Math.ceil(filteredItems.value.length / pageSize.value)
)

8. Exemplo completo: Lista dinâmica de produtos

▶ Exemplo: 1. 5 maneiras de usar v-if/v-show

VUE
<template>
 <!-- 1. v-if:Conditional Rendering(Not here DOM) -->
 <p v-if="show">v-if</p>
 
 <!-- 2. v-show:display none(Always there DOM) -->
 <p v-show="show">v-show</p>
 
 <!-- 3. v-else-if:Chained Conditions -->
 <p v-if="type === 'A'">A</p>
 <p v-else-if="type === 'B'">B</p>
 <p v-else>C</p>
 
 <!-- 4. v-if + template:Multi-element -->
 <template v-if="isAdmin">
 <h1>Admin</h1>
 <button>Edit</button>
 </template>
 
 <!-- 5. v-if vs v-show Performance Comparison -->
 <div v-if="occasionally">Switch Occasionally</div>
 <div v-show="frequently">Frequent switching(Not recommended for use in combination with `<transition>`,v-if That's the only way)</div>
</template>
▶ Experimente

▶ Exemplo: 2. 5 maneiras de usar v-for

VUE
<template>
 <!-- 1. Array -->
 <li v-for="item in items" :key="item.id">{{ item.name }}</li>
 
 <!-- 2. Array + Index -->
 <li v-for="(item, i) in items" :key="item.id">
 {{ i + 1 }}. {{ item.name }}
 </li>
 
 <!-- 3. Object -->
 <li v-for="(value, key) in user" :key="key">
 {{ key }}: {{ value }}
 </li>
 
 <!-- 4. Numbers -->
 <span v-for="n in 5" :key="n">{{ n }}</span>
 
 <!-- 5. String -->
 <span v-for="char in 'Hello'" :key="char">{{ char }}</span>
</template>
▶ Experimente

▶ Exemplo: 3. Comparação de 5 maneiras de usar :key

VUE
<template>
 <!-- 1. The Only One ID(Best) -->
 <li v-for="user in users" :key="user.id">{{ user.name }}</li>
 
 <!-- 2. Unique Field -->
 <li v-for="user in users" :key="user.email">{{ user.name }}</li>
 
 <!-- 3. Composite key -->
 <li v-for="post in posts" :key="`${post.userId}-${post.id}`">
 {{ post.title }}
 </li>
 
 <!-- 4. Use index (Second choice) -->
 <li v-for="(item, i) in items" :key="i">{{ item.name }}</li>
 
 <!-- 5. ❌ random()(Never, under any circumstances) -->
 <li v-for="item in items" :key="Math.random()">{{ item.name }}</li>
</template>
▶ Experimente

▶ Exemplo: 4. Exemplo completo de filtragem + ordenação + paginação de listas

VUE 📖 Somente leitura
<template>
 <div>
 <!-- Search box -->
 <input v-model="searchQuery" placeholder="Search...">
 
 <!-- Filter by Category -->
 <select v-model="selectedCategory">
 <option value="all">All</option>
 <option value="phone">Phone</option>
 <option value="laptop">Laptop</option>
 </select>
 
 <!-- Sort -->
 <select v-model="sortBy">
 <option value="price">Price</option>
 <option value="name">Name</option>
 </select>
 
 <!-- List -->
 <ul>
 <li v-for="item in paginatedItems" :key="item.id">
 {{ item.name }} - ${{ item.price }}
 </li>
 </ul>
 
 <!-- Pagination -->
 <button :disabled="currentPage === 1" @click="currentPage--">
 Previous
 </button>
 <span>{{ currentPage }} / {{ totalPages }}</span>
 <button :disabled="currentPage === totalPages" @click="currentPage++">
 Next
 </button>
 </div>
</template>

<script setup>
import { ref, computed } from 'vue'

const items = ref([
 { id: 1, name: 'iPhone', price: 999, category: 'phone' },
 { id: 2, name: 'MacBook', price: 2499, category: 'laptop' },
 { id: 3, name: 'iPad', price: 599, category: 'tablet' }
])

const searchQuery = ref('')
const selectedCategory = ref('all')
const sortBy = ref('price')
const currentPage = ref(1)
const pageSize = 10

const filteredItems = computed(() => {
 let result = items.value
 if (searchQuery.value) {
 result = result.filter(i => i.name.includes(searchQuery.value))
 }
 if (selectedCategory.value !== 'all') {
 result = result.filter(i => i.category === selectedCategory.value)
 }
 return [...result].sort((a, b) => {
 if (sortBy.value === 'price') return a.price - b.price
 return a.name.localeCompare(b.name)
 })
})

const paginatedItems = computed(() => {
 const start = (currentPage.value - 1) * pageSize
 return filteredItems.value.slice(start, start + pageSize)
})

const totalPages = computed(() =>
 Math.ceil(filteredItems.value.length / pageSize)
)
</script>
59 linhas de lógica (limite de 40, somente leitura)

▶ Exemplo: 5. 5 erros comuns

Erro Sintoma Solução
Nenhuma :key Desalinhamento da lista, baixo desempenho Adicionar :key="item.id"
Como usar v-for e v-if juntos Confusão sobre a prioridade do v-if Envolvendo com template ou usando computed
Use “índice” como chave Desalinhamento durante a classificação Use um ID exclusivo
O v-show não é compatível com <transition> Não é possível criar animações Use v-if em vez disso + <transition>
A lista é muito grande (mais de 100.000) A página trava Use o vue-virtual-scroller para rolagem virtual

▶ Exemplo: 6. Prioridade entre v-if e v-for

VUE
<!-- In Vue 3, v-if has higher priority than v-for (Vue 2 is the opposite) -->
<ul>
 <!-- Vue 3: First determine v-if, then v-for -->
 <li v-for="user in users" v-if="user.isActive" :key="user.id">
 {{ user.name }}
 </li>
 <!-- ✅ But Vue officially recommends using template to separate -->
 
 <!-- ❌ Incorrect wording(Not recommended) -->
 <li v-for="user in users" v-if="shouldShowUsers(user)" :key="user.id">
 {{ user.name }}
 </li>
 
 <!-- ✅ Recommendations:computed Filter -->
 <li v-for="user in activeUsers" :key="user.id">
 {{ user.name }}
 </li>
</template>
▶ Experimente

❓ Perguntas Frequentes

P: Como faço para escolher entre v-if e v-show? R: Use v-if para alternâncias ocasionais (para economizar na renderização inicial) e v-show para alternâncias frequentes (para evitar destruir e reconstruir o DOM repetidamente). Use v-if para estados de login/logout e v-show para alternância entre abas.

P: O :key é obrigatório quando se usa o v-for? R: Recomendamos enfaticamente o uso do :key para evitar problemas de desempenho e incompatibilidades no estado dos componentes. O ideal é usar um ID exclusivo (item.id), seguido pelo index; nunca use o random().

P: Qual é a prioridade ao usar v-if e v-for juntos? R: No Vue 3, o v-if tem precedência sobre o v-for (ao contrário do Vue 2). No entanto, a recomendação oficial é evitar usá-los juntos; em vez disso, separe-os usando o template ou filtre usando o computed.

P: Qual é a finalidade do elemento template? R: O template é um elemento wrapper lógico do Vue que não é renderizado no DOM. Ele é usado para evitar elementos div desnecessários quando v-if ou v-for incluem vários elementos.

P: O que posso fazer se o desempenho da lista for ruim? R: Três otimizações: (1) Adicione :key; (2) Use computed para armazenar em cache os resultados da filtragem/classificação; (3) Para listas grandes (mais de 100.000), use vue-virtual-scroller para rolagem virtual, a fim de renderizar apenas a área visível.

P: É possível acessar chaves e valores ao iterar sobre um objeto com v-for? R: Sim. v-for="(value, key, index) in obj", ordem dos parâmetros: valor, chave, índice.

P: Como faço para exibir uma mensagem de aviso quando não há dados? R: Use uma combinação de v-if e v-else: <div v-if="items.length">Column </div><div v-else> Data</div>.

P: Como faço para destacar um item selecionado em uma lista? R: Adicione um activeIndex a cada item: @click="activeIndex = item.id" :class="{ active: activeIndex === item.id }".


📖 Resumo


📝 Exercícios

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

    Use v-if/v-else para alternar entre os estados de conectado e desconectado:

    • Conectado: exibe o nome de usuário e o botão “Sair”
    • Não conectado: exibe a mensagem “Faça login” e o formulário de login
    • Use um template para agrupar vários elementos
  2. Problemas avançados (Dificuldade: ⭐⭐)

    Como criar uma lista de tarefas usando v-for:

    • 3 status: Todos / Em andamento / Concluídos (alterne entre eles usando os 3 botões)
    • A lista exibe as tarefas com o status correspondente
    • Cada item possui os botões “Concluir” e “Excluir”
    • A mensagem “Inacabado: N itens” é exibida na parte inferior
  3. Problema de desafio (Dificuldade: ⭐⭐⭐)

    Implementar um sistema completo de filtragem, ordenação e paginação de produtos (5 arquivos):

    1. Dados do produto: 30 registros, incluindo nome, preço, categoria e data de criação
    2. Pesquisa: Faça uma pesquisa aproximada por nome
    3. Filtrar por categoria: 5 categorias (todas/celular/laptop/tablet/fones de ouvido)
    4. Ordenar por: Preço (crescente/decrescente), Nome, Mais recentes
    5. Paginação: 10 itens por página, com navegação numérica entre as páginas
    6. Desempenho: armazena em cache os resultados calculados, filtrados e classificados
    7. Otimização de listas extensas: 1.000 itens de teste para garantir um desempenho sem interrupções
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%