Vue.js: Dados Reativos: ref/reactive
Última atualização: 2026-10-20
O sistema de reatividade do Vue 3 é o seu coração e alma — quando você modifica uma variável, todos os locais da página onde ela é usada são atualizados automaticamente. É por isso que o Vue é 10 vezes mais fácil de programar do que o jQuery. O sistema de reatividade é alimentado por duas APIs principais: ref() e reactive().
O ref é usado para envolver qualquer tipo de dado (números, strings, objetos), enquanto o reactive só pode envolver objetos. Os dois são usados de maneiras diferentes, e esta lição vai ajudar você a entender completamente a diferença.
1. O que você vai aprender
- A essência dos dados reativos: interceptação por proxy + coleta de dependências + acionamento de atualizações
ref()Sintaxe para encapsular qualquer value,.valueSintaxe para acessá-loreactive()Objeto de empacotamento, resposta automática de profundidade- As 5 principais diferenças entre “Ref” e “Reactive” e quando usar cada um
- shallowRef / shallowReactive Reatividade superficial
- Como usar as funções utilitárias toRef / toRefs
- 5 erros comuns que levam ao fracasso do design responsivo
2. A história de uma página de contagem que deu errado
(1) Problema: Quando a contagem muda, a interface do usuário não é atualizada
Alice escreveu seu primeiro componente em Vue 3 para o painel de administração do site de comércio eletrônico. Ela tentou implementar um “contador de estoque de produtos”:
// ❌ The "Broken" Version
let count = 0
function increment() {
count++
console.log(count) // 1, 2, 3... It looks like
}
<template>
<p>Stock: {{ count }}</p>
<button @click="increment">+1</button>
</template>
Ela clica no botão 5 vezes. O console exibe 1, 2, 3, 4, 5. Mas a página ainda mostra “Estoque: 0”.
A gerente de produto, Charlie, olha por cima do ombro:
“Alice, por que a contagem não atualiza? Eu cliquei cinco vezes. O Vue está com problema?”
(2) Soluções reativas do Vue: ref() / reactive()
Agora, ao clicar no botão, a página é atualizada instantaneamente. A alteração na variável → o DOM é atualizado automaticamente.
(3) Receita
Depois de aprender sobre a reatividade do Vue 3:
- Volume de código: 30 linhas de manipulação do DOM com jQuery → 5 linhas com Vue
- Taxa de erros: 50% (esquecimento de atualizar o DOM) → 0% (o Vue cuida disso)
- Carga cognitiva: “quais elementos do DOM dependem dessa variável?” → “todos eles, automaticamente”
3. Os princípios da reatividade: proxy + coleta de dependências
(1) Processo de resposta em três etapas
sequenceDiagram
participant T as Template
participant C as Component
participant R as Reactive<br/>(Proxy)
participant D as Data
T->>C: 1. Render (Read count)
C->>R: 2. get count
R->>D: 3. Read 0
R->>R: 4. Track: Who's using it? count?
R-->>C: 5. Return 0
C-->>T: 6. Display "Stock: 0"
Note over D,T: The user clicks the button
D->>R: 7. set count = 1
R->>R: 8. Trigger: Notice to All Users count the place
R->>C: 9. Re-render
C-->>T: 10. Display "Stock: 1"
(2) Pontos-chave
- Interceptação por proxy: O Vue 3 usa proxies ES6 para interceptar operações get/set
- Coleção de dependências: registra “quem utilizou esses dados” (rastreamento) quando o modelo é renderizado
- Acionar uma atualização: Notificar “todos os locais relevantes” para que sejam renderizados novamente quando os dados forem alterados (acionador)
(3) Vue 2 x Vue 3: Uma comparação da reatividade
| Dimensão | Vista 2 | Vista 3 |
|---|---|---|
| API subjacente | Object.defineProperty |
ES6 Proxy |
| Observadores de matriz | Requer tratamento especial | Suporte nativo |
| Mapa/Definir como favorito | Não compatível | Compatível |
| Adicionar novo imóvel | Requer Vue.set() |
Resposta automática |
| Desempenho | Média | Melhor (Lazy Proxy) |
4. ref(): Envolve qualquer valor
(1) Uso básico
import { ref } from 'vue'
// Packaging Numbers
const count = ref(0)
// Packaging String
const name = ref('Alice')
// Boolean Packaging
const isVip = ref(false)
// Packaging null
const data = ref(null)
// Packaging Array
const items = ref([1, 2, 3])
// Items to Be Packaged
const user = ref({ name: 'Bob', age: 25 })
(2) Acesso aos valores: use .value no JavaScript; o modelo os descompacta automaticamente
(3) A natureza “bidirecional” de ref
import { ref } from 'vue'
const count = ref(0)
// Read
console.log(count.value) // 0
// Edit
count.value = 1
console.log(count.value) // 1
// Modify Nested Properties(ref When wrapping an object)
const user = ref({ name: 'Bob' })
user.value.name = 'Alice' // ✅ Responsive
user.value = { name: 'Charlie' } // ✅ The entire replacement also responds
▶ Exemplo: Uso básico de ref
▶ Experimente
Saída:
Count: 0
Clique em +1 → Count: 1, 2, 3...
Clique em Reset → Count: 0
5. reactive(): Objeto wrapper (Reatividade profunda)
(1) Uso básico
import { reactive } from 'vue'
// Only objects can be passed
const state = reactive({
count: 0,
user: { name: 'Alice', age: 25 },
items: [1, 2, 3]
})
// No access required .value
console.log(state.count) // 0
console.log(state.user.name) // Alice
// Edit Auto-Reply
state.count = 1
state.user.name = 'Bob'
state.items.push(4)
(2) Resposta detalhada
const state = reactive({
level1: {
level2: {
level3: {
value: 'deep'
}
}
}
})
// Changes made at any level are automatically reflected
state.level1.level2.level3.value = 'updated' // ✅ Trigger an update
(3) Limitações da Programação Reativa
// ❌ You cannot directly replace the entire object.(No longer responsive)
let state = reactive({ count: 0 })
state = reactive({ count: 1 }) // Wrong! state variable itself changed, but template uses old reference.
// ❌ Deconstruction results in the loss of responsiveness
const state = reactive({ count: 0, name: 'Alice' })
const { count, name } = state // count/name Convert to a regular variable,Not responding
// ✅ Destructuring Reactivity: use toRefs
import { toRefs } from 'vue'
const { count, name } = toRefs(state) // count/name.value Stay Responsive
▶ Exemplo: Objetos wrapper reativos
▶ ExperimenteSaída:
User: Alice
Age: 25
Clique em +1 Year → Age: 26
6. As 5 principais diferenças entre ref e reactive
| Aspecto | ref | reativo |
|---|---|---|
| Tipos encapsuláveis | Qualquer valor (número, string, objeto, array) | Apenas objetos (Object/Array) |
| Método de acesso | .value (JS) |
Acesso direto (JS) |
| Acesso ao modelo | Desempacotamento automático (sem .value) | Acesso direto |
| Substituir tudo | ref.value = {...} ✅ Responde |
reactive({...}) = ... ❌ Não responde |
| Desconstrução | As respostas são preservadas (toRefs) | As respostas são perdidas (toRefs obrigatório) |
(4) Recomendações de seleção
| Cenário | Recomendação |
|---|---|
| Tipos primitivos (número/cadeia de caracteres/booleano) | ref() |
| Objeto único responsivo | É aceitável tanto ref({}) quanto reactive({}) |
| Um estado composto por vários campos relacionados | reactive({}) (como o Vuex) |
| Precisa ser substituído por completo | ref({}) (mais flexível) |
| Passagem de objetos / emissão usando | ref() (limites claros) |
7. Reatividade superficial: shallowRef / shallowReactive
(1) Quando se deve usar respostas superficiais?
Quando você tem um objeto grande, mas está interessado apenas nas alterações nos campos de nível superior, uma resposta superficial pode melhorar o desempenho.
import { shallowRef, shallowReactive, triggerRef } from 'vue'
// shallowRef:Follow Only .value Replace All,Do not track internal properties
const bigData = shallowRef({ items: [...50000 items] })
bigData.value.items.push('new') // ❌ Do not trigger an update
bigData.value = { items: [newItems] } // ✅ Trigger an update(Replace All)
// shallowReactive:Track only top-level properties,No in-depth response
const state = shallowReactive({
user: { name: 'Alice' },
settings: { theme: 'dark' }
})
state.user.name = 'Bob' // ❌ Does not trigger
state.user = { name: 'Bob' } // ✅ Trigger(Top-Level Property Replacement)
(2) Forçar uma atualização
import { triggerRef } from 'vue'
const bigData = shallowRef({ items: [...] })
bigData.value.items.push('new') // Do not respond by default
triggerRef(bigData) // Manually Trigger an Update
Cenários típicos: tabelas grandes (100.000 linhas), listas extensas e situações em que as propriedades internas são modificadas com frequência, mas apenas o estado geral é relevante.
▶ Exemplo: Resposta superficial + triggerRef
import { shallowRef, triggerRef } from 'vue'
// Large Table 100,000 rows → use shallowRef
const bigTable = shallowRef({
rows: Array.from({ length: 100000 }, (_, i) => ({ id: i, name: `Row ${i}` }))
})
// Replace All(Highly efficient)
function reloadData(newRows) {
bigTable.value = { rows: newRows }
}
// Modify Internal Properties(Not responding,Must be done manually trigger)
function updateRow(id, newName) {
const row = bigTable.value.rows.find(r => r.id === id)
if (row) {
row.name = newName
triggerRef(bigTable) // Manually Trigger an Update
}
}
Saída:
bigTable.value = { rows: newRows } → tabela renderizada de novo (rápido)
updateRow(1, 'Novo') → nome alterado e triggerRef força o re-render
Sem triggerRef → a alteração não aparece na tela
8. toRef / toRefs: Desconstrução com manutenção da reatividade
(1) toRefs: Desestruturação de um objeto reativo
import { reactive, toRefs } from 'vue'
const state = reactive({
count: 0,
name: 'Alice'
})
// ❌ General Deconstruction:Missing Response
const { count, name } = state
// ✅ toRefs Deconstruction:Stay Responsive
const { count, name } = toRefs(state)
// Used in templates
// <p>{{ count }}</p> <!-- Still responding -->
(2) toRef: Converte um valor comum em uma referência
import { toRef, ref } from 'vue'
// Scene: Select one field from props
const props = defineProps({ count: Number })
// ❌ Directly Deconstructing Missing Responses
const { count } = props // count It's ordinary number
// ✅ toRef Packaging
const count = toRef(props, 'count') // count is ref(props.count)
// Usage
count.value++ // Responsive Changes props
▶ Exemplo: toRefs — Desestruturação com preservação da resposta
▶ ExperimenteSaída:
0 - Alice
Clique em +1 → 1 - Alice
(count vem de toRefs, o template desembrulha o .value)
9. 5 Armadilhas do Design Responsivo
(1) Armadilha 1: Substituir diretamente um objeto reativo
let state = reactive({ count: 0 })
// ❌ state The entire variable has been changed,But the template uses the old version state
state = reactive({ count: 1 })
// ✅ Edit Properties
state.count = 1
(2) Armadilha 2: Desconstruir a reação e perder a resposta
const state = reactive({ count: 0 })
// ❌ count It is a regular variable
const { count } = state
// ✅ Use toRefs
const { count } = toRefs(state)
(3) Armadilha 3: Arrays com shallowRef não reagem a mutações
const items = shallowRef([1, 2, 3])
// ❌ shallowRef só rastreia substituição de .value, não mutações internas
items.value.push(4) // NÃO dispara re-renderização
// ✅ Substitua o array inteiro
items.value = [...items.value, 4]
// ✅ Ou force o trigger
items.value.push(4)
triggerRef(items)
// Nota: Com ref() normal, push É reativo (ref envolve arrays em proxy reativo)
const list = ref([1, 2, 3])
list.value.push(4) // ✅ Dispara re-renderização no Vue 3
(4) Armadilha 4: Modificar diretamente o conteúdo de um objeto encapsulado por um ref
// It generally does not expire.,However, there are edge cases
const user = ref({ name: 'Alice' })
user.value.name = 'Bob' // ✅ Response(ref Wrapped objects are automatically unwrapped)
(5) Armadilha 5: Confusão entre os tipos ref e reactive
// ❌ Type error:reactive Cannot wrap the original value
const count = reactive(0) // Error: value cannot be made reactive
// ✅ Use ref
const count = ref(0)
10. Exercício abrangente: comparação entre ref e reactive
▶ Exemplo: Comparação entre ref e reactive
import { ref, reactive } from 'vue'
// ref Style
const userRef = ref({ name: 'Alice', age: 25 })
// Edit:
userRef.value.name = 'Bob' // ✅ Response
userRef.value = { name: 'Charlie', age: 30 } // ✅ Response
// reactive Style
const userReactive = reactive({ name: 'Alice', age: 25 })
// Edit:
userReactive.name = 'Bob' // ✅ Response
// userReactive = reactive({...}) // ❌ Failure
Saída:
userRef.value.name = 'Bob' → reflete na tela
userRef.value = { name: 'Charlie' } → reflete na tela
userReactive.name = 'Bob' → reflete na tela
userReactive = reactive({...}) → a tela NÃO atualiza
(1) 📊 6. Referência rápida para 5 erros comuns
| Erro | Sintoma | Solução |
|---|---|---|
let state = reactive({...}); state = {...} |
Substituir todo o estado; o modelo não é atualizado | Modificar propriedade state.x = 1 |
const { count } = state Desconstrução |
Converter count em uma variável comum |
Usar toRefs(state) |
reactive(0) Valor da embalagem original |
Erro | Use ref() para o valor original |
count.value++ no modelo |
erro | modelo diretamente {{ count }} |
array[index] = x com shallowRef |
Não dispara (shallow só rastreia .value) | Use splice ou substitua o array inteiro |
❓ Perguntas Frequentes
ref ou reactive?ref (motivo: ref funciona com qualquer tipo e oferece uma substituição e passagem de props/funções mais claras no geral). reactive é adequado para cenários em que “o estado consiste em vários campos relacionados” (como o Vuex). Recomenda-se que os iniciantes aprendam primeiro ref e utilizem reactive quando estiverem mais proficientes..value, mas um template não?.value é usado para indicar explicitamente ao mecanismo de JavaScript: “Este é um valor envolvido por uma ref; ele precisa ser tornado reativo”. O Vue analisa os templates por conta própria; ele sabe quais elementos são refs e os desembrulha automaticamente. Resumindo: .value é necessário no JavaScript (para evitar ambiguidade), enquanto os modelos desembrulham automaticamente as refs (por conveniência)..value.x para acessar suas propriedades?ref({name: 'Alice'}), o modelo usa {{ user.name }} diretamente, e o JavaScript usa user.value.name. O Vue lida automaticamente com o acesso às propriedades internas do objeto envolvido pelo ref.toRefs(). Após const { count, name } = toRefs(state), count e name tornam-se referências; tanto {{ count }} no modelo quanto count.value no JavaScript permanecem reativos.ref é uma referência profunda (alterações nas propriedades internas também são acionadas), enquanto shallowRef rastreia apenas a substituição de .value como um todo (não rastreia alterações internas). Quando o conjunto de dados é grande (mais de 100.000 registros) e você está interessado apenas na substituição geral, usar shallowRef proporciona melhor desempenho.toRefs; (3) envolver um valor primitivo em um ref que não reage (use reactive em vez disso ou mantenha o ref); (4) criar um ref fora de um setup (fazendo com que ele perca a reatividade); (5) usar shallowRef/shallowReactive e esperar que mutações profundas disparem atualizações.reactive pode envolver o Map e o Set?reactive do Vue 3, que é implementado usando Proxy, suporta Map, Set, WeakMap e WeakSet. O Vue 2 não suporta isso.📖 Resumo
- O cerne do sistema de reatividade do Vue 3: interceptação por proxy + rastreamento de dependências + acionamento de atualizações
ref()Encapsula qualquer valor; requer.valuepara acesso via JavaScript; é automaticamente desempacotado pelo modeloreactive()Objeto encapsulado (Objeto/Matriz), resposta profunda, sem .value- Escolha entre
refereactive:refé versátil e recomendado;reactiveé adequado para o “estado multifield” - 5 principais armadilhas: Substituição de objetos reativos / Perda de respostas durante a desestruturação / Uso de reativo com valores primitivos / Atribuição direta a matrizes / Confusão de tipos
- shallowRef / shallowReactive são usados para otimização de desempenho com objetos grandes
- toRefs preserva a resposta durante a desestruturação; toRef converte um valor comum em uma referência
📝 Exercícios
-
Questões básicas (Dificuldade: ⭐)
Escreva um contador simples de quantidade para um carrinho de compras usando
ref:- Valor inicial 1
- Botão “+”: Adicionar 1
- O botão “-” diminui o valor em 1
- Desativar o botão “-” quando a quantidade for ≤ 0 (:desativado dinamicamente)
- Desativar o botão “+” quando a quantidade for ≥ 99
-
Problemas avançados (Dificuldade: ⭐⭐)
Crie um formulário para editar o perfil do usuário usando o Reactive:
- O campo “state” contém três campos:
name,emaileage - Vinculação bidirecional para três campos de entrada (usando o evento @input; abordaremos o v-model na próxima aula)
- O botão “Redefinir” restaura os valores padrão
- O botão “Enviar” exibe o estado completo no console
- O campo “state” contém três campos:
-
Problema desafiador (Dificuldade: ⭐⭐⭐)
Implementação de um aplicativo completo de tarefas (camada de dados):
- Envolva a matriz
todoscomreactive - Cada tarefa inclui
{ id, text, done } - Métodos disponíveis: addTodo(text) / toggleTodo(id) / removeTodo(id) / clearDone()
- Forneça as seguintes propriedades calculadas: activeCount (número de itens não concluídos) e doneCount (número de itens concluídos)
- Exibir a lista completa de tarefas + contagem no modelo
Dados iniciais:
{ id: 1, text: 'Learn Vue 3', done: false } - Envolva a matriz