Programação Web 2

Estado e Reatividade no Vue 3

Prof. Me. Danilo Bueno

O que vamos ver hoje

  1. Por que variável comum não funciona
  2. ref() — estado reativo simples
  3. computed() — valores derivados
  4. onMounted() — buscar dados no momento certo
  5. API REST e fetch com POST

O problema

Variável comum não avisa o Vue

<script setup>
let contador = 0;

function incrementar() {
  contador++; // Vue não vê essa mudança
}
</script>

<template>
  <p>{{ contador }}</p>
  <button @click="incrementar">+1</button>
</template>

A tela nunca atualiza.
O Vue precisa de uma forma de observar o valor.

ref() — estado reativo

ref() — a solução

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

const contador = ref(0);

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

<template>
  <p>{{ contador }}</p>
  <!-- sem .value no template -->
  <button @click="incrementar">+1</button>
</template>

.value é obrigatório no script — o Vue desempacota automaticamente no template.

ref() funciona com qualquer tipo

const nome = ref("Ana");      // string
const ativo = ref(false);     // boolean
const itens = ref([]);        // array
const produto = ref(null);    // começa vazio

Use ref() como padrão.
Funciona com qualquer valor.

computed() — valores derivados

computed() — quando um valor depende de outro

import { ref, computed } from "vue";

const produtos = ref([]);

const totalProdutos = computed(() => produtos.value.length);

const totalEmPromocao = computed(() => {
  let contador = 0;
  for (const p of produtos.value) {
    if (p.promocao) contador++;
  }
  return contador;
});

Recalcula só quando produtos muda. Resultado é cacheado.

computed vs método

// computed — cachea o resultado, só recalcula quando muda
const totalProdutos = computed(() => produtos.value.length);

// método — recalcula a cada re-renderização
function getTotalProdutos() {
  return produtos.value.length;
}

Use computed quando o valor depende de estado reativo.

onMounted() — ciclo de vida

onMounted() — executar ao aparecer na tela

onMounted executa uma vez, logo após o componente aparecer na tela.
É o lugar certo para buscar dados de uma API.

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

const musicas = ref([]);

onMounted(async () => {
  const res = await fetch("https://api.exemplo.com/musicas");
  musicas.value = await res.json();
});
</script>

Consumo de API REST

O que é uma API REST?

Cliente e servidor trocam dados por HTTP — normalmente em JSON.

Em REST, operamos sobre recursos (produtos, usuários, pedidos...).
A intenção vem de método + URL.

Método Intenção Exemplo
GET Ler GET /products
POST Criar POST /products
PUT/PATCH Atualizar PUT /products/10
DELETE Remover DELETE /products/10

Anatomia de uma requisição HTTP

método   URL
  ↓       ↓
POST  /products
Headers: { Content-Type: application/json }
Body:    { "nome": "Teclado", "preco": 199.9 }

Resposta da API:

Status: 201 Created
Body:   { "id": 42, "nome": "Teclado", "preco": 199.9 }

JSON.stringify — objeto JS → texto JSON

O body de uma requisição HTTP é texto.
JSON.stringify converte o objeto antes de enviar.

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

console.log(produto);
// { nome: 'Teclado', preco: 199.9, emEstoque: true }

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

Na resposta, res.json() faz o caminho inverso — texto JSON → objeto JS.

fetch com POST

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");

  const criado = await res.json();
  itens.value.push(criado); // atualiza estado local
}

Após o POST: adicione o retorno ao array com push. Não refaz o GET.

fetch com DELETE

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");
}

@submit.prevent — formulário sem reload

JavaScript puro (já visto antes):

form.addEventListener("submit", (event) => {
  event.preventDefault(); // impede reload da página
  salvar();
});

No Vue, é a mesma coisa com sintaxe mais curta:

<form @submit.prevent="salvar">

.prevent é atalho para event.preventDefault().

Referência rápida

Recurso Para que serve
ref(valor) Estado reativo simples
computed(() => ...) Valor derivado de estado
onMounted(() => ...) Executar ao montar o componente
JSON.stringify(obj) Objeto JS → texto JSON (para o body)
res.json() Texto JSON da resposta → objeto JS
@submit.prevent Evitar reload no submit do formulário

Obrigado!