DEV Community

Helton Carlos Brito Silva
Helton Carlos Brito Silva

Posted on

Vue 3 e Keep alive

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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)