Vue.js: Dados Reativos: ref/reactive
Última atualização: 2026-08-26
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()
<!-- ✅ Correct Version -->
<template>
<p>Stock: {{ count }}</p>
<button @click="increment">+1</button>
</template>
<script setup>
import { ref } from 'vue'
// Use ref() Wrapper → Vue Knows it should be \"Reactive\"
const count = ref(0)
function increment() {
// In JS Key Points .value
count.value++
// Automatic unpacking in templates,Not necessary .value
}
</script>
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
<template>
<!-- Using variable names directly in templates(Automatic Unpacking) -->
<p>{{ count }}</p> <!-- 0 -->
<p>{{ name }}</p> <!-- Alice -->
<p>{{ user.name }}</p> <!-- Bob -->
</template>
<script setup>
import { ref } from 'vue'
const count = ref(0)
const name = ref('Alice')
const user = ref({ name: 'Bob', age: 25 })
function logCount() {
// In JS Must use .value
console.log(count.value) // 0
console.log(name.value) // Alice
console.log(user.value.name) // Bob
}
</script>
(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
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
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.
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
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. Exemplo completo: carrinho de compras responsivo
▶ Exemplo: 1. Uso básico de ref
<template>
<div>
<p>Count: {{ count }}</p>
<button @click="increment">+1</button>
<button @click="reset">Reset</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
const count = ref(0)
function increment() {
count.value++ // JS Must be used in .value
}
function reset() {
count.value = 0
}
</script>
▶ Exemplo: 2. Objetos wrapper reativos
<template>
<div>
<p>User: {{ state.user.name }}</p>
<p>Age: {{ state.user.age }}</p>
<button @click="birthday">+1 Year</button>
</div>
</template>
<script setup>
import { reactive } from 'vue'
const state = reactive({
user: { name: 'Alice', age: 25 }
})
function birthday() {
state.user.age++ // Edit directly,Automatic Response
}
</script>
▶ Exemplo: 3. 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
▶ Exemplo: 4. toRefs — Desestruturação com preservação da resposta
<template>
<div>
<p>{{ count }} - {{ name }}</p>
<button @click="increment">+1</button>
</div>
</template>
<script setup>
import { reactive, toRefs } from 'vue'
const state = reactive({ count: 0, name: 'Alice' })
const { count, name } = toRefs(state) // Stay Responsive
function increment() {
count.value++ // count is ref, So use .value
}
</script>
▶ Exemplo: 5. 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
}
}
▶ Exemplo: 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
P: O que devo escolher:
refoureactive? R: A recomendação oficial é usar exclusivamenteref(motivo:reffunciona 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 primeirorefe utilizemreactivequando estiverem mais proficientes.
P: Por que uma ref precisa de
.value, mas um template não? R:.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).
P: Ao usar um ref para envolver um objeto, preciso usar
.value.xpara acessar suas propriedades? R: Não. Apósref({name: 'Alice'}), o modelo usa{{ user.name }}diretamente, e o JavaScript usauser.value.name. O Vue lida automaticamente com o acesso às propriedades internas do objeto envolvido pelo ref.
P: A desestruturação reativa faz com que a reatividade seja perdida? Como isso pode ser resolvido? R: Envolva-a com
toRefs(). Apósconst { count, name } = toRefs(state),countenametornam-se referências; tanto{{ count }}no modelo quantocount.valueno JavaScript permanecem reativos.
P: Qual é a diferença entre shallowRef e ref? R:
refé uma referência profunda (alterações nas propriedades internas também são acionadas), enquantoshallowRefrastreia apenas a substituição de.valuecomo 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.
P: Quais são as causas mais comuns para a falha do comportamento reativo? R: As 5 mais comuns são: (1) substituir diretamente um objeto reativo; (2) desestruturar um objeto reativo sem usar
toRefs; (3) envolver um valor primitivo em umrefque não reage (usereactiveem vez disso ou mantenha oref); (4) criar umreffora de umsetup(fazendo com que ele perca a reatividade); (5) usarshallowRef/shallowReactivee esperar que mutações profundas disparem atualizações.
P: O
reactivepode envolver oMape oSet? R: Sim. Oreactivedo Vue 3, que é implementado usandoProxy, suportaMap,Set,WeakMapeWeakSet. 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