Capítulo 02: Reatividade Básica: ref(), reactive(), toRefs() e Desestruturação
Capítulo 02: Reatividade Básica: ref(), reactive(), toRefs() e Desestruturação
Especialização em Front-End com Vue • Vue 3 & Nuxt 3 • Composition API, Pinia, Vue Router, SSR e TypeScript
🗺️ Mapa Conceitual da Reatividade Vue 3
flowchart TD
A["Estado Declarado: ref(v) ou reactive(obj)"] --> B["Vue Reactivity Engine (ES6 Proxy)"]
B --> C["Operação de Leitura: get()"]
B --> D["Operação de Escrita: set()"]
C --> E["track(): Registra Efeito Ativo (Dep Map)"]
D --> F["trigger(): Notifica Assinantes e Dispara Render"]
E & F --> G["Reconciliação e Atualização Cirúrgica do DOM"]
subgraph REF_VS_REACTIVE["Diferenciação de Tipos"]
H["ref<T>: Envelopamento .value (Primitivos e Objetos)"]
I["reactive<T>: Proxy Profundo (Apenas Objetos)"]
J["toRefs(obj): Converte chaves em Refs individuais"]
K["shallowRef<T>: Rastreia apenas nova atribuição"]
end
B --> REF_VS_REACTIVE
style A fill:#e1f5fe,stroke:#03a9f4,stroke-width:2px
style B fill:#fff3e0,stroke:#ff9800,stroke-width:2px
style G fill:#e8f5e9,stroke:#4caf50,stroke-width:2px
style REF_VS_REACTIVE fill:#f3e5f5,stroke:#9c27b0,stroke-width:2px
🏛️ 1. O Mecanismo Interno do Sistema de Reatividade (ES6 Proxy)
Diferente do Vue 2, que interceptava mutações através de Object.defineProperty (impondo limitações graves como a incapacidade de detectar adição de propriedades ou mutações em índices de arrays), o Vue 3 utiliza Proxies nativos do JavaScript (ES6).
1.1 Rastreamento de Dependências: track() e trigger()
O núcleo da reatividade reativa apoia-se em duas operações determinísticas:
track(target, key): Quando uma propriedade reativa é lida durante a execução de uma função de renderização, de umcomputedou de umwatchEffect, o Vue registra a função corrente como dependente ativa (Subscriber) associada àquela chave em umWeakMapglobal.trigger(target, key): Quando uma propriedade recebe um novo valor, a armadilhasetdo Proxy localiza todas as dependências cadastradas noWeakMape as enfileira na fila de microtarefas do navegador para reavaliação.
1.2 ref() vs reactive(): Regras Arquiteturais
ref<T>(): Cria um objeto de referência reativo contendo a propriedade.value. Funciona tanto com tipos primitivos (string,number,boolean,symbol) quanto com estruturas complexas. No<template>, o compilador SFC realiza o desempacotamento automático (auto-unwrapping), dispensando a escrita de.value.reactive<T>(): Cria um Proxy direto sobre o objeto alvo. Funciona exclusivamente com tipos de referência (Object,Array,Map,Set). Não possui propriedade.value.
1.3 O Perigo da Perda de Reatividade na Desestruturação
Ao executar a desestruturação padrão do JavaScript em um objeto reactive:
// ❌ ANTI-PATTERN: QUEBRA DE REATIVIDADE
const state = reactive({ count: 0, user: 'Admin' });
const { count, user } = state; // 'count' torna-se uma variável numérica primitiva desacoplada do Proxy!
Para manter a ligação com o Proxy, o desenvolvedor deve utilizar toRefs(state) ou toRef(state, 'count'), que convertem as propriedades em referências do tipo Ref<T> sincronizadas com o objeto original.
⚖️ 2. Tabela Comparativa de Estruturas Reativas
| Característica | ref(val) |
reactive(obj) |
shallowRef(val) |
|---|---|---|---|
| Tipos Suportados | Primitivos e Objetos/Arrays. | Apenas Objetos e Coleções (Map, Set). |
Primitivos, Objetos ou Arrays volumosos. |
| Acesso ao Valor | .value no <script>, direto no <template>. |
Acesso direto às propriedades sem .value. |
.value obrigatório para atribuição. |
| Profundidade (Depth) | Reatividade Profunda (Deep Reactivity). | Reatividade Profunda (Deep Reactivity). | Apenas superficial (não reage a mutações internas). |
| Desestruturação Segura | Mantém reatividade direta. | Quebra reatividade sem toRefs(). |
Exige substituição da referência raiz. |
| Casos de Uso Ideais | Estado atômico, formulários, contadores, IDs. | Estados agrupados de domínio, modelos de dados. | Logs, tabelas com 50.000+ linhas, instâncias de SDKs. |
⚠️ 3. Armadilhas Comuns em Produção (Gotchas & Anti-Patterns)
Anti-Pattern 1: Esquecer .value em Lógica Condicional dentro de <script setup>
Erro Comum: Escrever if (isOpen) ao invés de if (isOpen.value). Como isOpen é um objeto de referência (wrapper), o JavaScript avalia o objeto como truthy mesmo que o valor interno seja false.
Abordagem Correta: Adote regras de lint como @typescript-eslint/strict-boolean-expressions para impedir que Ref<boolean> seja avaliado diretamente sem .value.
Anti-Pattern 2: Sobrescrever a Variável do reactive()
Erro Comum:
let userProfile = reactive({ id: 1, name: 'Ana' });
// Tentativa de atualizar após retorno da API:
userProfile = { id: 2, name: 'Carlos' }; // Destrói o Proxy original; o template deixa de atualizar!
Abordagem Correta: Use ref para o objeto raiz (const userProfile = ref(...) e atualize com userProfile.value = novoObjeto) ou utilize Object.assign(userProfile, novoObjeto).
💻 4. Código Conceitual Arquitetural Comentado
<!-- src/components/DataEngine.vue -->
<script setup lang="ts">
import { ref, reactive, toRefs, shallowRef, triggerRef } from 'vue';
// 1. Ref simples para controle de flag primitiva
const isLoading = ref<boolean>(false);
// 2. Estado corporativo complexo via reactive
interface ServerClusterConfig {
datacenter: string;
maxNodes: number;
tags: string[];
}
const clusterState = reactive<ServerClusterConfig>({
datacenter: 'us-central-1',
maxNodes: 64,
tags: ['production', 'pci-dss']
});
// 3. Desestruturação com toRefs garantindo reatividade bidirecional contínua
const { datacenter, maxNodes } = toRefs(clusterState);
// 4. shallowRef para grande massa de telemetria sem sobrecarga de Proxy recursivo
const telemetryLogs = shallowRef<string[]>(['10:00:00 - Inicialização do nó 01']);
function pushTelemetryEvent(): void {
// Em shallowRef, mutações in-place não disparam reatividade por padrão
telemetryLogs.value.push(`${new Date().toLocaleTimeString()} - Evento de checagem de saúde`);
// Força atualização explícita do DOM
triggerRef(telemetryLogs);
}
function scaleCluster(): void {
// Alteração refletida tanto em maxNodes quanto em clusterState.maxNodes
maxNodes.value += 8;
}
</script>
<template>
<div class="max-w-md mx-auto p-6 bg-white border rounded-2xl shadow-sm space-y-4">
<h3 class="font-bold text-slate-800 text-lg">Cluster: {{ datacenter }}</h3>
<p class="text-sm text-slate-600">Nós Máximos: <strong class="text-indigo-600">{{ maxNodes }}</strong></p>
<div class="flex gap-2">
<button
@click="scaleCluster"
class="px-3 py-1.5 text-xs font-semibold bg-indigo-600 text-white rounded-lg hover:bg-indigo-700"
>
Expandir Nós (+8)
</button>
<button
@click="pushTelemetryEvent"
class="px-3 py-1.5 text-xs font-semibold bg-slate-100 text-slate-700 rounded-lg hover:bg-slate-200"
>
Registrar Telemetria (triggerRef)
</button>
</div>
<div class="bg-slate-900 text-emerald-400 p-3 rounded-xl font-mono text-xs max-h-32 overflow-y-auto">
<div v-for="(log, idx) in telemetryLogs" :key="idx">{{ log }}</div>
</div>
</div>
</template>
🛠️ 5. Cheat-Sheet de Reatividade Vue 3
| Função | Sintaxe Canônica | Comportamento Principal |
|---|---|---|
ref(x) |
const n = ref(10) |
Envelopa qualquer valor em { value: 10 }. Desempacotado no template. |
reactive(obj) |
const s = reactive({ a: 1 }) |
Cria Proxy profundo. Não use em primitivos. Cuidado com desestruturação. |
toRefs(obj) |
const { a } = toRefs(s) |
Converte cada chave do objeto reativo em uma Ref conectada. |
toRef(obj, 'k') |
const a = toRef(s, 'a') |
Converte uma propriedade única em uma Ref. |
shallowRef(x) |
const l = shallowRef([...]) |
Rastreia apenas a reatribuição l.value = [...]. Otimização para listas gigantes. |
triggerRef(r) |
triggerRef(l) |
Força disparo manual de reatividade para uma shallowRef. |