Recentemente, me deparei com um problema frustrante em um cenário muito específico de navegação por abas. A tela possuía duas abas principais: uma de Dúvidas (FAQ) e outra com um Formulário.
O gargalo acontecia durante o preenchimento: se o usuário estivesse no meio do formulário, tivesse uma dúvida e clicasse na aba de Dúvidas para ler algo, ao retornar para a aba do Formulário, todos os dados digitados haviam desaparecido. O componente era destruído e recriado do zero.
A Solução Inicial (e trabalhosa):
Minha primeira intuição foi recorrer a uma abordagem clássica: salvar os dados momentaneamente no localStorage a cada alteração e, ao final do envio do formulário, limpar essa chave. Embora funcione, é uma solução que exige muito código manual e gerenciamento de estado externo.
A Solução Elegante com Vue:
Buscando uma alternativa mais limpa, lembrei-me do . No Vue, ele é um componente embutido (built-in) e abstrato, o que significa que ele não renderiza nenhum elemento HTML real como uma tag div. Ele atua apenas como um "gerente invisível" no Virtual DOM, instruindo o Vue a fazer o cache do componente em vez de destruí-lo ao trocar de aba. Assim, o formulário e suas informações permanecem intactos na memória, melhorando a experiência do usuário com pouquíssimo esforço.
1º Página que recebe as abas
<script setup lang="ts">
import { shallowRef } from "vue";
import AbaFormulario from "../components/AbaFormulario.vue";
import AbaDuvidas from "../components/AbaDuvidas.vue";
const abaAtual = shallowRef(AbaFormulario);
</script>
<template>
<div class="container-abas">
<nav class="botoes-abas">
<button
:class="{ ativo: abaAtual === AbaFormulario }"
@click="abaAtual = AbaFormulario"
>
Preencher Formulário
</button>
<button
:class="{ ativo: abaAtual === AbaDuvidas }"
@click="abaAtual = AbaDuvidas"
>
Dúvidas Frequentes
</button>
</nav>
<div class="conteudo-aba">
<KeepAlive>
<component :is="abaAtual" />
</KeepAlive>
</div>
</div>
</template>
<style scoped>
.ativo {
font-weight: bold;
background-color: #e0e0e0;
color: var(--black);
}
.botoes-abas {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
</style>
O KeepAlive ele envolve o componentes dinâmico, no exemplo uso abas, mas poderia ser páginas também, pode ser qualquer arquivo .vue
2º Componente de AbaFormulario
<script setup lang="ts">
import { ref, onActivated, onDeactivated } from "vue";
const formData = ref({
nome: "",
email: "",
mensagem: "",
});
onActivated(() => {
console.log("Formulário ATIVADO (Recuperado do cache)");
});
onDeactivated(() => {
console.log("Formulário DESATIVADO (Guardado no cache)");
});
</script>
<template>
<div class="formulario-container">
<h2>Preencha seus dados</h2>
<p>Comece a digitar, troque de aba e volte. Seus dados estarão aqui!</p>
<form @submit.prevent>
<div class="campo">
<label>Nome:</label>
<input
v-model="formData.nome"
type="text"
placeholder="Digite seu nome"
/>
</div>
<div class="campo">
<label>E-mail:</label>
<input
v-model="formData.email"
type="email"
placeholder="Digite seu e-mail"
/>
</div>
<div class="campo">
<label>Mensagem:</label>
<textarea
v-model="formData.mensagem"
rows="4"
placeholder="Sua dúvida ou solicitação..."
></textarea>
</div>
<button type="button">Enviar</button>
</form>
</div>
</template>
<style scoped>
.formulario-container {
padding: 20px;
border: 1px solid #ccc;
border-radius: 8px;
}
.campo {
margin-bottom: 15px;
display: flex;
flex-direction: column;
}
input,
textarea {
padding: 8px;
margin-top: 5px;
border: 1px solid #999;
border-radius: 4px;
}
button {
padding: 10px 15px;
background-color: #4caf50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
</style>
Veja que usamos o console.log para ver os gatilhos do ciclo.
onActivated()
Regista uma função de resposta a ser chamada depois da instância do componente for inserida no DOM como parte duma árvore armazenada para consulta imediata pelo .
onDeactivated()
Regista uma função de resposta a ser chamada depois da instância do componente ser removida do DOM como parte duma árvore armazenada para consulta imediata pelo .
3º Componente de AbaDuvidas
<script setup lang="ts"></script>
<template>
<div class="duvidas-container">
<h2>Dúvidas Frequentes (FAQ)</h2>
<p>Aqui estão as respostas para as perguntas mais comuns.</p>
<div class="faq-item">
<h3>Preciso preencher todos os campos do formulário?</h3>
<p>
Sim, todos os campos são obrigatórios para que possamos entrar em
contato de forma eficiente.
</p>
</div>
<div class="faq-item">
<h3>Qual o prazo de resposta?</h3>
<p>Nossa equipe geralmente responde em até 24 horas úteis.</p>
</div>
</div>
</template>
<style scoped>
.duvidas-container {
padding: 20px;
border: 1px solid #ccc;
border-radius: 8px;
background-color: var(--black);
}
.faq-item {
margin-top: 15px;
padding-bottom: 10px;
border-bottom: 1px dashed #ccc;
}
h3 {
margin-bottom: 5px;
color: var(--white);
font-size: 1.1em;
}
</style>
Cuidados com o keep alive
Caso você precise carregar a página com reload, ele irá perde os dados, já que ele fica guardado nos componentes em memória (cache).
Usamos o keep alive no lado do cliente, ótimo em aplicações SPA.
Referência:
https://pt.vuejs.org/guide/built-ins/keep-alive
https://learnvue.co/articles/vue-keep-alive
https://pt.vuejs.org/api/composition-api-lifecycle
Top comments (0)