Search buenodc

Nesta página
  1. 1. O problema — por que variável comum não funciona
  2. 2. ref() — tornando um valor reativo
  3. 3. reactive() — você vai encontrar isso
  4. 4. computed() — propriedades derivadas
  5. 5. onMounted() — executar código ao carregar o componente
  6. 6. Consumo de API REST com fetch
  7. 7. Referência rápida

Estado e Reatividade no Vue 3

Quando você usa o Instagram e o número de curtidas muda sem recarregar a página, ou adiciona um produto ao carrinho e o ícone atualiza na hora — por baixo está acontecendo algo simples: um dado mudou e a tela reagiu.

Para construir interfaces assim, dois conceitos precisam estar claros antes de qualquer código:

Estado é o conjunto de dados que o app precisa acompanhar ao longo da sessão do usuário: quais séries estão carregadas, o texto digitado num campo de busca, se um formulário está aberto ou fechado. Toda interface interativa tem estado — a questão é como gerenciá-lo.

Reatividade é o mecanismo que mantém a tela sincronizada com esse estado automaticamente. Em vez de você manipular o DOM na mão toda vez que um valor muda, você declara o estado, escreve o template usando ele e o Vue assume a responsabilidade de atualizar apenas as partes da tela que dependem daquele dado.

Mas para o sistema de reatividade do Vue funcionar, ele precisa saber que um valor mudou — e JavaScript puro não avisa ninguém quando uma variável é reatribuída. Por isso o Vue oferece ferramentas próprias para declarar estado: ref e computed. É o que você vai aprender aqui.


1. O problema — por que variável comum não funciona

Em JavaScript, quando você altera o valor de uma variável, o navegador não faz nada automaticamente — a tela não atualiza sozinha.

O Vue precisa saber que um valor mudou para re-renderizar o template. Uma variável let comum é invisível para o Vue.

<!-- ❌ Isso não funciona — a tela nunca atualiza -->
<template>
  <p>{{ contador }}</p>
  <button @click="incrementar">+1</button>
</template>

<script setup>
let contador = 0;

function incrementar() {
  contador++; // Vue não sabe que isso mudou
}
</script>

A solução é embrulhar o valor em um objeto reativo usando ref().


2. ref() — tornando um valor reativo

A solução é envolver o valor dentro de um objeto especial que o Vue sabe observar. Toda vez que você modifica o conteúdo desse objeto, o Vue detecta a mudança e re-renderiza o template. Pense nele como uma caixa transparente: o Vue fica de olho na caixa, e quando o conteúdo muda, o Vue age.

Esse objeto é criado com ref(). Quando o valor interno muda, o template re-renderiza automaticamente.

<!-- ✅ Funciona — Vue observa o ref e atualiza a tela -->
<template>
  <p>{{ contador }}</p>
  <button @click="incrementar">+1</button>
</template>

<script setup>
import { ref } from "vue";

const contador = ref(0);

function incrementar() {
  contador.value++; // .value é obrigatório no script
}
</script>

Regra importante: no <script setup>, acesse o valor com .value. No <template>, o Vue desempacota automaticamente — escreva apenas {{ contador }}.

const nome = ref("Ana");

console.log(nome.value); // "Ana"
nome.value = "Carlos"; // atualiza e re-renderiza

Use ref() para qualquer tipo de dado: string, número, boolean, array ou objeto.

const itens = ref([]); // array vazio
const ativo = ref(false); // boolean
const produto = ref(null); // começa sem valor

3. reactive() — você vai encontrar isso

Você vai encontrar reactive() em tutoriais e projetos existentes. Ele também torna objetos reativos, mas sem .value — as propriedades são acessadas diretamente (form.titulo em vez de form.value.titulo).

A limitação prática é que reactive() perde a reatividade se você substituir o objeto inteiro ou desestruturar suas propriedades fora do template. Por isso, ref() é a escolha padrão no Vue 3 moderno — funciona com qualquer tipo de valor e não tem essas armadilhas.

Neste curso usamos ref() para tudo.


4. computed() — propriedades derivadas

Pense numa planilha: você tem uma coluna de preço, outra de quantidade e uma terceira de total. Você não preenche o total manualmente — ele é uma fórmula que calcula automaticamente a partir das outras colunas. Se o preço muda, o total atualiza sozinho.

computed é exatamente esse mecanismo no Vue: um valor que deriva de outros valores reativos. Você define a regra uma vez e o Vue mantém o resultado atualizado. O resultado também é armazenado em cache — se as dependências não mudaram desde o último cálculo, o Vue reutiliza o valor já calculado em vez de rodar a função de novo.

<template>
  <p>Total de séries: {{ totalSeries }}</p>
  <p>Séries assistidas: {{ totalAssistidas }}</p>
</template>

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

const series = ref([]);

const totalSeries = computed(() => series.value.length);
const totalAssistidas = computed(() => {
  let contador = 0;
  for (const serie of series.value) {
    if (serie.assistida) contador++;
  }
  return contador;
});
</script>

computed vs método comum:

// computed — só recalcula quando a dependência muda
const totalSeries = computed(() => series.value.length);
const totalAssistidas = computed(() => {
  let contador = 0;
  for (const serie of series.value) {
    if (serie.assistida) contador++;
  }
  return contador;
});

// método — recalcula toda vez que o template re-renderiza
function getTotalSeries() {
  return series.value.length;
}

Use computed quando o valor depende de estado reativo e pode ser cacheado.


5. onMounted() — executar código ao carregar o componente

O ciclo de vida de um componente

Todo componente Vue passa por etapas previsíveis desde que é exibido até ser removido da tela:

  1. Criação — o Vue processa o <script setup>, inicializa variáveis e estado reativo
  2. Montagem — o Vue gera o HTML a partir do template e insere na página (o componente aparece na tela)
  3. Atualização — sempre que o estado muda, o template re-renderiza automaticamente
  4. Desmontagem — o componente é removido da tela (por exemplo, ao navegar para outra rota)

O Vue oferece funções especiais — chamadas de hooks de ciclo de vida — que permitem executar código em momentos específicos desse processo. Você registra um callback e o Vue o chama na hora certa.

onMounted é o hook da etapa 2: executa uma função uma única vez, logo após o componente aparecer na tela.

onMounted(() => {
  // executa uma vez, quando o componente for inserido na página
});

Por que onMounted é o lugar certo para buscar dados de uma API?

A ideia é simples: o componente aparece primeiro (com a lista vazia ou um texto de “Carregando…”), depois o onMounted dispara o fetch. Quando os dados chegam, o estado é atualizado e o Vue re-renderiza automaticamente.

<template>
  <ul>
    <li v-for="musica in musicas" :key="musica.id">
      {{ musica.titulo }}
    </li>
  </ul>
</template>

<script setup>
import { ref, onMounted } from "vue";

const musicas = ref([]); // 1. começa vazio — lista aparece vazia

onMounted(async () => {
  // 2. dispara após o componente aparecer
  const res = await fetch("https://api.exemplo.com/musicas");
  musicas.value = await res.json(); // 3. atualiza o estado → Vue re-renderiza
});
</script>

Se você colocasse o fetch diretamente no topo do <script setup> (fora do onMounted), funcionaria na maioria dos casos simples — mas o onMounted é a forma idiomática do Vue, deixa explícito o momento em que a operação acontece e evita problemas em situações mais complexas (acesso ao DOM, componentes filhos, plugins).


6. Consumo de API REST com fetch

Até aqui, vimos como o estado muda no front-end. Agora entra o próximo passo: buscar e enviar dados para um backend.

O que é uma API REST?

Uma API REST (Representational State Transfer) é uma forma padronizada de comunicação entre cliente (front-end) e servidor via HTTP.

Na prática:

  • o cliente faz uma requisição HTTP
  • o servidor devolve uma resposta HTTP
  • os dados geralmente trafegam em JSON

Em REST, trabalhamos com recursos (products, users, posts, orders etc.) e cada recurso costuma ter uma URL base (endpoint), por exemplo: /products.

Métodos HTTP mais comuns

  • GET: ler dados
  • POST: criar novo recurso
  • PUT/PATCH: atualizar recurso existente
  • DELETE: remover recurso

Em REST, a intenção da operação vem de método + URL.

Exemplos:

  • GET /products -> listar produtos
  • GET /products/10 -> buscar produto id 10
  • POST /products -> criar produto
  • DELETE /products/10 -> remover produto id 10

Requisição e resposta

Uma requisição HTTP costuma ter:

  • URL: destino
  • method: ação
  • headers: metadados (ex.: tipo de conteúdo)
  • body: dados enviados (quando necessário)

Uma resposta HTTP costuma ter:

  • status code (ex.: 200, 201, 404, 500)
  • headers
  • body com os dados retornados (muitas vezes JSON)

Status comuns em APIs REST:

  • 200 OK -> sucesso em leitura/atualização
  • 201 Created -> recurso criado
  • 204 No Content -> sucesso sem corpo de resposta (comum em DELETE)
  • 400 Bad Request -> dados inválidos
  • 404 Not Found -> recurso não encontrado
  • 500 Internal Server Error -> erro no servidor

Fluxo de formulário -> requisição -> resposta JSON

Fluxo típico de cadastro:

  1. usuário preenche um formulário
  2. front-end monta um objeto JS com os dados
  3. fetch envia POST com body
  4. API processa e responde JSON
  5. front converte com await res.json() e atualiza o estado

Esse ciclo é a base do consumo de API no front-end.

O papel do JSON no envio de dados

O body da requisição HTTP é texto. Como no front trabalhamos com objetos JS, precisamos converter antes de enviar.

Para isso usamos JSON.stringify(...).

const produto = { nome: "Teclado", preco: 199.9, emEstoque: true };

console.log(produto);
// objeto JavaScript

const payload = JSON.stringify(produto);
console.log(payload);
// "{\"nome\":\"Teclado\",\"preco\":199.9,\"emEstoque\":true}"

Ao receber a resposta, fazemos o caminho inverso com res.json().

const res = await fetch("https://api.exemplo.com/products");
const dados = await res.json();
// dados volta a ser objeto/array JavaScript

Exemplo simples com fetch

async function criarProduto(produto) {
  const res = await fetch("https://api.exemplo.com/products", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(produto),
  });

  if (!res.ok) throw new Error("Erro ao criar produto");
  return await res.json();
}
async function removerProduto(id) {
  const res = await fetch(`https://api.exemplo.com/products/${id}`, {
    method: "DELETE",
  });

  if (!res.ok) throw new Error("Erro ao remover produto");
}

@submit.prevent e event.preventDefault()

Em formulários HTML, o envio padrão recarrega a página.

Em JavaScript puro, vocês já viram:

form.addEventListener("submit", (event) => {
  event.preventDefault();
  salvar();
});

No Vue, a ideia é exatamente a mesma, só muda a sintaxe:

<form @submit.prevent="salvar">

O .prevent é atalho para event.preventDefault().


7. Referência rápida

RecursoImportaçãoUso
ref(valor)import { ref } from "vue"Estado reativo simples
computed(() => ...)import { computed } from "vue"Valor derivado de estado
onMounted(() => ...)import { onMounted } from "vue"Executar ao montar o componente
.value—Acessar/modificar ref no script